Conversation
zelig
left a comment
There was a problem hiding this comment.
Radical changes needed, see comments
you should always run test with the --race flag. That shall reveal all I am saying in the review commenst
| write chan elements.Node // hands out current root for writes and locks | ||
| root chan elements.Node // channel for new roots | ||
| quit chan struct{} // closing this channel signals quit | ||
| closed bool |
There was a problem hiding this comment.
ok so this is not needed, in fact checking this field creates a race condition if Close is called concurrently with any other function that checks it.
| write: make(chan elements.Node), | ||
| root: make(chan elements.Node), | ||
| quit: make(chan struct{}), | ||
| closed: false, |
There was a problem hiding this comment.
this assignment is unnecessary since the zero value of boolean is false
| for { | ||
| select { | ||
| case <-quit: | ||
| idx.closed = true |
There was a problem hiding this comment.
this is a write write race with close
| func (idx *Index) Update(ctx context.Context, k []byte, e *elements.Entry) error { | ||
| var root elements.Node | ||
|
|
||
| if idx.closed { |
There was a problem hiding this comment.
this is read/write race on the boolean closed
| if idx.closed { | ||
| return errors.New("trie closed") | ||
| } | ||
|
|
There was a problem hiding this comment.
but it is even worse cos if Close is called when we are here concurrently then the following select will hang on line 114 until the context cancels :))
|
|
||
| // Iterate wraps the underlying pot's iterator | ||
| func (idx *Index) Iterate(ctx context.Context, p, k []byte, f func(elements.Entry) (stop bool, err error)) error { | ||
| if idx.closed { |
|
|
||
| // Close quits the process loop and closes the mode | ||
| func (idx *Index) Close() error { | ||
| if idx.closed { |
There was a problem hiding this comment.
if you are protecting here from a concurrent call to Close then it is not gonna work and you can still get a close on closed channel error
|
|
||
| // Size returns the size (number of entries) of the pot | ||
| func (idx *Index) Size() int { | ||
| func (idx *Index) Size() (int, error) { |
There was a problem hiding this comment.
so here you dont need to return an error at all and this is where you should see that the pattern is wrong. So there is no reason one cannot call Size on a closed pot
|
|
||
| // String pretty prints the current state of the pot | ||
| func (idx *Index) String() string { | ||
| func (idx *Index) String() (string, error) { |
There was a problem hiding this comment.
same as Size, Save : no reason why not
|
|
||
| // Save calls the mode specific save method for the root node | ||
| func (idx *Index) Save(ctx context.Context) ([]byte, error) { | ||
| if idx.closed { |
There was a problem hiding this comment.
like Size, no reason not to allow Save on a closed
|
I separate out the core intent, adding The t.Run("add item to closed index", func(t *testing.T) {
idx.Close()
idx.Add(ctx, want)
})
t.Run("find item in closed index", func(t *testing.T) {
idx.Add(ctx, want)
idx.Close()
idx.Find(ctx, want.Key())
})
t.Run("double close index", func(t *testing.T) {
idx.Close()
idx.Close()
})The first two hang, the third panics. The full test is checked in on branch The For the purposes of using this in WASM, there is no problem at all, because WASM is single-threaded. A WASM setup -- like POT JS -- also has a more dire need for this protection as a hanging WASM object effectively 'deletes methods' as well as taking out state. The WASM process going down should be avoided where possible. Yet worse, the garbage collection hooks of Javascript ( https://github.com/brainiac-five/potjs/blob/f9b57daab12563093be5ea32f6788d768e93a379/potjs.go#L276 -- so that a developer might not even be at fault for a Relevantly, while not perfect, this proposal was less intrusive; a proper solution seems to mean wrapping everything in mutexes. That degree of change looked inappropriate. But that should be implemented then, to better protect POT JS users. The Adding the check for closed indices also to |
Adding the missing Close() function to KVS.
fixes #18
Most changes are to the Index class that receives a flag and a check for wether the index instance -- i.e. its mutex loop -- is already closed. This avoids hanging requests to index that would occur whenever an index whose mutex loop does not run anymore (not) receives a lock request.