[#274] wc: Resolve possible deadlock

If operation with WC are _fast enough_ (e.g. `Init` failed and `Close` is
called immediately) there is a race and a deadlock that do not allow finish
(and start, in fact) an initialization routine because of taken `modeMtx`
and also do not allow finish `Close` call because of awaiting initialization
finish. So do stop initialization _before_ any mutex is taken.

Signed-off-by: Pavel Karpy <p.karpy@yadro.com>
This commit is contained in:
Pavel Karpy 2023-04-20 18:51:16 +03:00
parent 6b6f33ed71
commit e9461686b8
2 changed files with 10 additions and 13 deletions

View file

@ -29,9 +29,6 @@ func (c *cache) SetMode(m mode.Mode) error {
)) ))
defer span.End() defer span.End()
c.modeMtx.Lock()
defer c.modeMtx.Unlock()
return c.setMode(ctx, m) return c.setMode(ctx, m)
} }
@ -40,13 +37,6 @@ func (c *cache) setMode(ctx context.Context, m mode.Mode) error {
var err error var err error
turnOffMeta := m.NoMetabase() turnOffMeta := m.NoMetabase()
if turnOffMeta && !c.mode.NoMetabase() {
err = c.flush(ctx, true)
if err != nil {
return err
}
}
if !c.initialized.Load() { if !c.initialized.Load() {
close(c.stopInitCh) close(c.stopInitCh)
@ -60,6 +50,16 @@ func (c *cache) setMode(ctx context.Context, m mode.Mode) error {
}() }()
} }
c.modeMtx.Lock()
defer c.modeMtx.Unlock()
if turnOffMeta && !c.mode.NoMetabase() {
err = c.flush(ctx, true)
if err != nil {
return err
}
}
if c.db != nil { if c.db != nil {
if err = c.db.Close(); err != nil { if err = c.db.Close(); err != nil {
return fmt.Errorf("can't close write-cache database: %w", err) return fmt.Errorf("can't close write-cache database: %w", err)

View file

@ -163,9 +163,6 @@ func (c *cache) Init() error {
// Close closes db connection and stops services. Executes ObjectCounters.FlushAndClose op. // Close closes db connection and stops services. Executes ObjectCounters.FlushAndClose op.
func (c *cache) Close() error { func (c *cache) Close() error {
c.modeMtx.Lock()
defer c.modeMtx.Unlock()
// Finish all in-progress operations. // Finish all in-progress operations.
if err := c.setMode(context.TODO(), mode.ReadOnly); err != nil { if err := c.setMode(context.TODO(), mode.ReadOnly); err != nil {
return err return err