@@ -21,8 +21,6 @@ import (
2121 "path/filepath"
2222 "regexp"
2323 "strconv"
24- "sync"
25- "sync/atomic"
2624 "time"
2725
2826 "github.com/tarantool/go-tarantool/v2"
@@ -77,25 +75,10 @@ type StartOpts struct {
7775 Dialer tarantool.Dialer
7876}
7977
80- // errorWrap should be used with a pointer - as `optional` type.
81- type errorWrap struct {
82- err error
83- }
84-
85- type statusInstance struct {
86- // status if nil, then process is not done yet.
87- status * errorWrap
88- // isStopping is a flag that terminate was internally initiated.
89- isStopping atomic.Bool
90- // waitMutex is used to prevent several invokes of the "Wait"
91- // for the same process.
92- // https://github.com/golang/go/issues/28461
93- waitMutex sync.RWMutex
94- }
95-
96- // statusInstanceWrap used to avoid capturing content in defer function.
97- type statusInstanceWrap struct {
98- impl * statusInstance
78+ type state struct {
79+ done chan struct {}
80+ ret error
81+ stoped bool
9982}
10083
10184// TarantoolInstance is a data for instance graceful shutdown and cleanup.
@@ -109,64 +92,75 @@ type TarantoolInstance struct {
10992 // Dialer to check that connection established.
11093 Dialer tarantool.Dialer
11194
112- st * statusInstanceWrap
113- }
114-
115- func newTarantoolInstance () TarantoolInstance {
116- return TarantoolInstance {st : & statusInstanceWrap {& statusInstance {}}}
95+ st chan state
11796}
11897
11998func (t * TarantoolInstance ) IsExit () bool {
120- succeeded := t .st .impl .waitMutex .TryRLock ()
121- if ! succeeded {
122- // Due to mutex locked by goroutine, it means that process running.
99+ st := <- t .st
100+ t .st <- st
101+
102+ select {
103+ case <- st .done :
104+ default :
123105 return false
124106 }
125- defer t . st . impl . waitMutex . RUnlock ()
126- return t . st .impl . status != nil
107+
108+ return st .ret != nil
127109}
128110
129111func (t * TarantoolInstance ) result () error {
130- succeeded := t .st .impl .waitMutex .TryRLock ()
131- if ! succeeded {
112+ st := <- t .st
113+ t .st <- st
114+
115+ select {
116+ case <- st .done :
117+ default :
132118 return nil
133119 }
134- defer t . st . impl . waitMutex . RUnlock ()
135- return t . st .impl . status . err
120+
121+ return st .ret
136122}
137123
138124func (t * TarantoolInstance ) checkDone () {
139- if t .st == nil || t .st .impl == nil {
140- panic ("TarantoolInstance is not initialized properly." )
141- }
142-
143125 go func () {
144- t .st .impl .waitMutex .Lock ()
145- defer t .st .impl .waitMutex .Unlock ()
146- t .st .impl .status = & errorWrap {t .Cmd .Wait ()}
147- if ! t .st .impl .isStopping .Load () {
126+ ret := t .Cmd .Wait ()
127+
128+ st := <- t .st
129+
130+ st .ret = ret
131+ close (st .done )
132+
133+ t .st <- st
134+
135+ if ! st .stoped {
148136 log .Printf ("Tarantool %q was unexpected terminated: %v" , t .Opts .Listen , t .result ())
149137 }
150138 }()
151139}
152140
153141func (t * TarantoolInstance ) Wait () error {
154- if t .st == nil {
155- panic ("TarantoolInstance is not initialized" )
156- }
157- t .st .impl .waitMutex .RLock ()
158- defer t .st .impl .waitMutex .RUnlock ()
159- // Note: don't call `result()` here to avoid double locking.
160- return t .st .impl .status .err
142+ st := <- t .st
143+ t .st <- st
144+
145+ <- st .done
146+
147+ st = <- t .st
148+ t .st <- st
149+
150+ return st .ret
161151}
162152
163153func (t * TarantoolInstance ) Stop () error {
164154 if t == nil {
165155 log .Print ("ASSERT: no Tarantool instance" )
166156 return nil
167157 }
158+
159+ st := <- t .st
160+ st .stoped = true
161+ t .st <- st
162+
168163 log .Printf ("Stopping Tarantool instance %q" , t .Opts .Listen )
169- t .st .impl .isStopping .Store (true )
170164 if t .IsExit () {
171165 log .Printf ("Already stopped instance %q with result: %v" , t .Opts .Listen , t .result ())
172166 return nil
@@ -295,9 +289,10 @@ func IsTarantoolEE() (bool, error) {
295289func RestartTarantool (inst * TarantoolInstance ) error {
296290 log .Printf ("Restarting Tarantool instance %q" , inst .Opts .Listen )
297291 startedInst , err := StartTarantool (inst .Opts )
292+
298293 inst .Cmd .Process = startedInst .Cmd .Process
299- // We can't change pointer to status instance, cause it could captured by `defer`.
300- inst . st . impl = startedInst . st . impl
294+ inst . st = startedInst . st
295+
301296 return err
302297}
303298
@@ -342,11 +337,17 @@ func prepareDir(workDir string) (string, error) {
342337// StartTarantool starts a tarantool instance for tests
343338// with specifies parameters (refer to StartOpts).
344339// Process must be stopped with StopTarantool.
345- func StartTarantool (startOpts StartOpts ) (TarantoolInstance , error ) {
340+ func StartTarantool (startOpts StartOpts ) (* TarantoolInstance , error ) {
346341 // Prepare tarantool command.
347- inst := newTarantoolInstance ()
348- var err error
342+ inst := & TarantoolInstance {
343+ st : make (chan state , 1 ),
344+ }
345+ init := state {
346+ done : make (chan struct {}),
347+ }
348+ inst .st <- init
349349
350+ var err error
350351 inst .Dialer = startOpts .Dialer
351352 startOpts .WorkDir , err = prepareDir (startOpts .WorkDir )
352353 if err != nil {
@@ -426,13 +427,13 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
426427
427428 if inst .IsExit () && inst .result () != nil {
428429 StopTarantool (inst )
429- return TarantoolInstance {}, fmt .Errorf ("unexpected terminated Tarantool %q: %w" ,
430+ return & TarantoolInstance {}, fmt .Errorf ("unexpected terminated Tarantool %q: %w" ,
430431 inst .Opts .Listen , inst .result ())
431432 }
432433
433434 if err != nil {
434435 StopTarantool (inst )
435- return TarantoolInstance {}, fmt .Errorf ("failed to connect Tarantool %q: %w" ,
436+ return & TarantoolInstance {}, fmt .Errorf ("failed to connect Tarantool %q: %w" ,
436437 inst .Opts .Listen , err )
437438 }
438439
@@ -442,7 +443,7 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
442443// StopTarantool stops a tarantool instance started
443444// with StartTarantool. Waits until any resources
444445// associated with the process is released. If something went wrong, fails.
445- func StopTarantool (inst TarantoolInstance ) {
446+ func StopTarantool (inst * TarantoolInstance ) {
446447 err := inst .Stop ()
447448 if err != nil {
448449 log .Fatal (err )
@@ -453,7 +454,7 @@ func StopTarantool(inst TarantoolInstance) {
453454// with StartTarantool. Waits until any resources
454455// associated with the process is released.
455456// Cleans work directory after stop. If something went wrong, fails.
456- func StopTarantoolWithCleanup (inst TarantoolInstance ) {
457+ func StopTarantoolWithCleanup (inst * TarantoolInstance ) {
457458 StopTarantool (inst )
458459
459460 if inst .Opts .WorkDir != "" {
0 commit comments