From 3b823ed00a22e142e25137dccb5f1c4fbb318d3f Mon Sep 17 00:00:00 2001
From: Semyon Krestyaninov <krestyaninov@perx.ru>
Date: Mon, 27 May 2024 11:32:09 +0000
Subject: [PATCH] =?UTF-8?q?fix(core):=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20deadlock=20=D0=BF=D1=80=D0=B8=20?=
 =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D0=BE=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD?=
 =?UTF-8?q?=D0=BD=D0=BE=D0=BC=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=B5=20Buf?=
 =?UTF-8?q?feredWriteSyncer.Stop=20=D0=B8=20=D1=81=D0=B8=D0=BD=D1=85=D1=80?=
 =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE?=
 =?UTF-8?q?=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D0=B2=D0=B0=D0=BB=D1=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Close #PRXS-2576
---
 logs/zap/buffered_write_syncer.go | 36 ++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/logs/zap/buffered_write_syncer.go b/logs/zap/buffered_write_syncer.go
index e862a79d..42fae2b1 100644
--- a/logs/zap/buffered_write_syncer.go
+++ b/logs/zap/buffered_write_syncer.go
@@ -71,28 +71,40 @@ func (ws *BufferedWriteSyncer) start() {
 	ws.buffer = make([]*logs.Entry, 0, ws.MaxBufferSize)
 	ws.syncQueue = make(chan []*logs.Entry, ws.MaxSyncQueueSize)
 	ws.flushStop = make(chan struct{})
+	ws.started = true
 
 	ws.wg.Add(2)
 	go ws.syncLoop()
 	go ws.flushLoop()
-
-	ws.started = true
 }
 
+// Stop останавливает все фоновые работы и синхронизирует оставшиеся записи
 func (ws *BufferedWriteSyncer) Stop() error {
-	ws.mu.Lock()
-	defer ws.mu.Unlock()
+	// Создаем новую область видимости для блокировки мьютекса только в указанной секции
+	// Это необходимо, чтобы предотвратить возможный deadlock, который может возникнуть
+	// если после блокировки мьютекса при остановке будет вызван Sync внутри flushLoop(),
+	// который будет ожидать освобождения мьютекса
+	stopped, err := func() (bool, error) {
+		ws.mu.Lock()
+		defer ws.mu.Unlock()
+
+		if !ws.started || ws.stopped {
+			return false, nil
+		}
+		ws.stopped = true
 
-	if !ws.started || ws.stopped {
-		return nil
-	}
-	ws.stopped = true
+		close(ws.flushStop) // завершаем flushLoop
+
+		err := ws.flush() // очищаем оставшиеся записи
 
-	close(ws.flushStop) // завершаем flushLoop
+		close(ws.syncQueue) // завершаем syncLoop
 
-	err := ws.flush() // очищаем оставшиеся записи
+		return true, err
+	}()
 
-	close(ws.syncQueue) // завершаем syncLoop
+	if !stopped {
+		return nil
+	}
 
 	ws.wg.Wait() // дожидаемся завершения flushLoop и syncLoop
 
@@ -127,7 +139,7 @@ func (ws *BufferedWriteSyncer) Sync() error {
 	ws.mu.Lock()
 	defer ws.mu.Unlock()
 
-	if ws.started {
+	if ws.started && !ws.stopped {
 		return ws.flush()
 	}
 
-- 
GitLab