From fcfd63e36416726e23da7af87554944298d6a10a Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sun, 12 Sep 2021 19:48:02 -0400 Subject: [PATCH] fix #1589: server "stop()" waits for active builds --- CHANGELOG.md | 4 ++++ pkg/api/serve_other.go | 48 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7dc4047682..6b0f282530e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ I would also like to add data for Safari. They have recently added support for arbitrary module namespace identifier names (https://bugs.webkit.org/show_bug.cgi?id=217576) and `export * as` (https://bugs.webkit.org/show_bug.cgi?id=214379). However, I have no idea how to determine which Safari release these bugs correspond to so this compatibility data for Safari has been omitted. +* Avoid unnecessary additional log messages after the server is stopped ([#1589](https://github.com/evanw/esbuild/issues/1589)) + + There is a development server built in to esbuild which is accessible via the `serve()` API call. This returns a promise that resolves to an object with a `stop()` method that immediately terminates the development server. Previously calling this could cause esbuild to print stray log messages since `stop()` could cause plugins to be unregistered while a build is still in progress. With this release, calling `stop()` no longer terminates the development server immediately. It now waits for any active builds to finish first so the builds are not interrupted and left in a confusing state. + ## 0.12.26 * Add `--analyze` to print information about the bundle ([#1568](https://github.com/evanw/esbuild/issues/1568)) diff --git a/pkg/api/serve_other.go b/pkg/api/serve_other.go index 4abdf5d2c8a..f7f08f8dba2 100644 --- a/pkg/api/serve_other.go +++ b/pkg/api/serve_other.go @@ -33,6 +33,8 @@ type apiHandler struct { rebuild func() BuildResult currentBuild *runningBuild fs fs.FS + serveWaitGroup sync.WaitGroup + serveError error } type runningBuild struct { @@ -547,6 +549,9 @@ func serveImpl(serveOptions ServeOptions, buildOptions BuildOptions) (ServeResul } } + var stoppingMutex sync.Mutex + isStopping := false + // The first build will just build normally var handler *apiHandler handler = &apiHandler{ @@ -554,6 +559,14 @@ func serveImpl(serveOptions ServeOptions, buildOptions BuildOptions) (ServeResul outdirPathPrefix: outdirPathPrefix, servedir: serveOptions.Servedir, rebuild: func() BuildResult { + stoppingMutex.Lock() + defer stoppingMutex.Unlock() + + // Don't start more rebuilds if we were told to stop + if isStopping { + return BuildResult{} + } + build := buildImpl(buildOptions) if handler.options == nil { handler.options = &build.options @@ -563,17 +576,38 @@ func serveImpl(serveOptions ServeOptions, buildOptions BuildOptions) (ServeResul fs: realFS, } - // Start the server + // When wait is called, block until the server's call to "Serve()" returns + result.Wait = func() error { + handler.serveWaitGroup.Wait() + return handler.serveError + } + + // Create the server server := &http.Server{Addr: addr, Handler: handler} - wait := make(chan error, 1) - result.Wait = func() error { return <-wait } - result.Stop = func() { server.Close() } + + // When stop is called, block further rebuilds and then close the server + result.Stop = func() { + stoppingMutex.Lock() + defer stoppingMutex.Unlock() + + // Only try to close the server once + if isStopping { + return + } + isStopping = true + + // Close the server and wait for it to close + server.Close() + handler.serveWaitGroup.Wait() + } + + // Start the server and signal on "serveWaitGroup" when it stops + handler.serveWaitGroup.Add(1) go func() { if err := server.Serve(listener); err != http.ErrServerClosed { - wait <- err - } else { - wait <- nil + handler.serveError = err } + handler.serveWaitGroup.Done() }() // Start the first build shortly after this function returns (but not