diff --git a/lib/renderer/bridge/bridge.go b/lib/renderer/bridge/bridge.go index 2559c306..85a9cfd9 100644 --- a/lib/renderer/bridge/bridge.go +++ b/lib/renderer/bridge/bridge.go @@ -42,12 +42,12 @@ type Bridge struct { server *http.Server lock sync.Mutex - sessions map[string]session + sessions map[string]*session } // Initialise the Bridge Renderer func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error { - h.sessions = map[string]session{} + h.sessions = map[string]*session{} h.ipcManager = ipcManager h.appConfig = appConfig h.eventManager = eventManager @@ -70,13 +70,11 @@ func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) { } func (h *Bridge) startSession(conn *websocket.Conn) { - s := session{ - conn: conn, - bindingCache: h.bindingCache, - ipc: h.ipcManager, - log: logger.NewCustomLogger("BridgeSession"), - eventManager: h.eventManager, - } + s := newSession(conn, + h.bindingCache, + h.ipcManager, + logger.NewCustomLogger("BridgeSession"), + h.eventManager) conn.SetCloseHandler(func(int, string) error { h.log.Infof("Connection dropped [%s].", s.Identifier()) @@ -160,7 +158,7 @@ func (h *Bridge) NotifyEvent(event *messages.EventData) error { } message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data) - dead := []session{} + dead := []*session{} for _, session := range h.sessions { err := session.evalJS(message, notifyMessage) if err != nil { @@ -207,6 +205,9 @@ func (h *Bridge) SetTitle(title string) { // for the Renderer interface func (h *Bridge) Close() { h.log.Debug("Shutting down") + for _, session := range h.sessions { + session.Shutdown() + } err := h.server.Close() if err != nil { h.log.Errorf(err.Error()) diff --git a/lib/renderer/bridge/session.go b/lib/renderer/bridge/session.go index fd4d9d59..1f575f5d 100644 --- a/lib/renderer/bridge/session.go +++ b/lib/renderer/bridge/session.go @@ -1,7 +1,8 @@ package renderer import ( - "sync" + "time" + "unsafe" "github.com/gorilla/websocket" "github.com/leaanthony/mewn" @@ -20,7 +21,22 @@ type session struct { ipc interfaces.IPCManager // Mutex for writing to the socket - lock sync.Mutex + shutdown chan bool + writeChan chan []byte + + done bool +} + +func newSession(conn *websocket.Conn, bindingCache []string, ipc interfaces.IPCManager, logger *logger.CustomLogger, eventMgr interfaces.EventManager) *session { + return &session{ + conn: conn, + bindingCache: bindingCache, + ipc: ipc, + log: logger, + eventManager: eventMgr, + shutdown: make(chan bool), + writeChan: make(chan []byte), + } } // Identifier returns a string identifier for the remote connection. @@ -33,19 +49,15 @@ func (s *session) Identifier() string { } func (s *session) sendMessage(msg string) error { - s.lock.Lock() - defer s.lock.Unlock() - - if err := s.conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil { - s.log.Debug(err.Error()) - return err + if !s.done { + s.writeChan <- *(*[]byte)(unsafe.Pointer(&msg)) } - return nil } func (s *session) start(firstSession bool) { s.log.Infof("Connected to frontend.") + go s.writePump() wailsRuntime := mewn.String("../../runtime/assets/wails.js") s.evalJS(wailsRuntime, wailsRuntimeMessage) @@ -74,6 +86,10 @@ func (s *session) start(firstSession bool) { s.log.Debugf("Got message: %#v\n", string(buffer)) s.ipc.Dispatch(string(buffer), s.Callback) + + if s.done { + break + } } } @@ -83,8 +99,38 @@ func (s *session) Callback(data string) error { } func (s *session) evalJS(js string, mtype messageType) error { - // Prepend message type to message return s.sendMessage(mtype.toString() + js) - +} + +// Shutdown +func (s *session) Shutdown() { + s.done = true + s.shutdown <- true + s.log.Debugf("session %v exit", s.Identifier()) +} + +// writePump pulls messages from the writeChan and sends them to the client +// since it uses a channel to read the messages the socket is protected without locks +func (s *session) writePump() { + s.log.Debugf("Session %v - writePump start", s.Identifier()) + for { + select { + case msg, ok := <-s.writeChan: + s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) + if !ok { + s.log.Debug("writeChan was closed!") + s.conn.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + + if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil { + s.log.Debug(err.Error()) + return + } + case <-s.shutdown: + break + } + } + s.log.Debug("writePump exiting...") }