@@ -21,8 +21,6 @@ import (
21
21
"path/filepath"
22
22
"regexp"
23
23
"strconv"
24
- "sync"
25
- "sync/atomic"
26
24
"time"
27
25
28
26
"github.com/tarantool/go-tarantool/v2"
@@ -77,25 +75,10 @@ type StartOpts struct {
77
75
Dialer tarantool.Dialer
78
76
}
79
77
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
99
82
}
100
83
101
84
// TarantoolInstance is a data for instance graceful shutdown and cleanup.
@@ -109,64 +92,75 @@ type TarantoolInstance struct {
109
92
// Dialer to check that connection established.
110
93
Dialer tarantool.Dialer
111
94
112
- st * statusInstanceWrap
113
- }
114
-
115
- func newTarantoolInstance () TarantoolInstance {
116
- return TarantoolInstance {st : & statusInstanceWrap {& statusInstance {}}}
95
+ st chan state
117
96
}
118
97
119
98
func (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 :
123
105
return false
124
106
}
125
- defer t . st . impl . waitMutex . RUnlock ()
126
- return t . st .impl . status != nil
107
+
108
+ return st .ret != nil
127
109
}
128
110
129
111
func (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 :
132
118
return nil
133
119
}
134
- defer t . st . impl . waitMutex . RUnlock ()
135
- return t . st .impl . status . err
120
+
121
+ return st .ret
136
122
}
137
123
138
124
func (t * TarantoolInstance ) checkDone () {
139
- if t .st == nil || t .st .impl == nil {
140
- panic ("TarantoolInstance is not initialized properly." )
141
- }
142
-
143
125
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 {
148
136
log .Printf ("Tarantool %q was unexpected terminated: %v" , t .Opts .Listen , t .result ())
149
137
}
150
138
}()
151
139
}
152
140
153
141
func (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
161
151
}
162
152
163
153
func (t * TarantoolInstance ) Stop () error {
164
154
if t == nil {
165
155
log .Print ("ASSERT: no Tarantool instance" )
166
156
return nil
167
157
}
158
+
159
+ st := <- t .st
160
+ st .stoped = true
161
+ t .st <- st
162
+
168
163
log .Printf ("Stopping Tarantool instance %q" , t .Opts .Listen )
169
- t .st .impl .isStopping .Store (true )
170
164
if t .IsExit () {
171
165
log .Printf ("Already stopped instance %q with result: %v" , t .Opts .Listen , t .result ())
172
166
return nil
@@ -295,9 +289,10 @@ func IsTarantoolEE() (bool, error) {
295
289
func RestartTarantool (inst * TarantoolInstance ) error {
296
290
log .Printf ("Restarting Tarantool instance %q" , inst .Opts .Listen )
297
291
startedInst , err := StartTarantool (inst .Opts )
292
+
298
293
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
+
301
296
return err
302
297
}
303
298
@@ -342,11 +337,17 @@ func prepareDir(workDir string) (string, error) {
342
337
// StartTarantool starts a tarantool instance for tests
343
338
// with specifies parameters (refer to StartOpts).
344
339
// Process must be stopped with StopTarantool.
345
- func StartTarantool (startOpts StartOpts ) (TarantoolInstance , error ) {
340
+ func StartTarantool (startOpts StartOpts ) (* TarantoolInstance , error ) {
346
341
// 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
349
349
350
+ var err error
350
351
inst .Dialer = startOpts .Dialer
351
352
startOpts .WorkDir , err = prepareDir (startOpts .WorkDir )
352
353
if err != nil {
@@ -426,13 +427,13 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
426
427
427
428
if inst .IsExit () && inst .result () != nil {
428
429
StopTarantool (inst )
429
- return TarantoolInstance {}, fmt .Errorf ("unexpected terminated Tarantool %q: %w" ,
430
+ return & TarantoolInstance {}, fmt .Errorf ("unexpected terminated Tarantool %q: %w" ,
430
431
inst .Opts .Listen , inst .result ())
431
432
}
432
433
433
434
if err != nil {
434
435
StopTarantool (inst )
435
- return TarantoolInstance {}, fmt .Errorf ("failed to connect Tarantool %q: %w" ,
436
+ return & TarantoolInstance {}, fmt .Errorf ("failed to connect Tarantool %q: %w" ,
436
437
inst .Opts .Listen , err )
437
438
}
438
439
@@ -442,7 +443,7 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
442
443
// StopTarantool stops a tarantool instance started
443
444
// with StartTarantool. Waits until any resources
444
445
// associated with the process is released. If something went wrong, fails.
445
- func StopTarantool (inst TarantoolInstance ) {
446
+ func StopTarantool (inst * TarantoolInstance ) {
446
447
err := inst .Stop ()
447
448
if err != nil {
448
449
log .Fatal (err )
@@ -453,7 +454,7 @@ func StopTarantool(inst TarantoolInstance) {
453
454
// with StartTarantool. Waits until any resources
454
455
// associated with the process is released.
455
456
// Cleans work directory after stop. If something went wrong, fails.
456
- func StopTarantoolWithCleanup (inst TarantoolInstance ) {
457
+ func StopTarantoolWithCleanup (inst * TarantoolInstance ) {
457
458
StopTarantool (inst )
458
459
459
460
if inst .Opts .WorkDir != "" {
0 commit comments