-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
io-sim: Fix flushTQueue implementation #135
Conversation
traceTQueueDefault | ||
:: MonadTraceSTM m | ||
=> proxy m | ||
-> TQueueDefault m a | ||
-> (Maybe [a] -> [a] -> InspectMonad m TraceValue) | ||
-> STM m () | ||
traceTQueueDefault p (TQueue read write) f = do | ||
traceTVar p read | ||
(\mas as -> f mas as) | ||
traceTVar p write | ||
(\mas as -> f mas as) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong, because f
has access to either side (read / write) of the queue, rather than all its elements. This is the reason why I kept the default implementation of TQueueDefault
in a single TVar
instead of two. This approach requires more substantial changes to io-sim
.
I think it's much simpler to just fix flushTQueueDefault
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you suggest keeping the differing implementation using a single TVar
? Why is tracing a deciding factor here - can't we implement this on the standard implementation of read/write end queues?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it is designed for verification purposes, so it's quite crucial for the library.
So you suggest keeping the differing implementation using a single TVar?
Yes, keep the current implementation which btw is based on a standard queue data type (see Okasaki's book). stm
splits it into two TVar
's I think for optimisation purposes, e.g. some operations will only need access one of the TVar
s which might change the contention in a parallel scenario - in io-sim
we don't have any parallelism, so it's not a factor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One invariant that one might want to be able to verify is the number of elements in a queue (if the application has such an invariant). It will be impossible to write it with the proposed traceTQueueDefault
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, let me try to write a flushTQueue
which works on the single TVar
.
I don't understand traceTQueueDefault
and if you say we cannot implement it correctly for the standard double TVar implementation, I need to trust you.
But I do see the continued risk of maintaining a "differing" implementation on io-sim
which does not correspond to the IO/STM variant semantics - if we could "just" keep lifted copies of the base
and stm
implementations around, that would be much more maintainable by casual contributors like me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in ch1bo@9e2444d, but while doing so I discovered another bug (which underlines my worry of having non-standard implementation): #136
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it not make sense to have an "equivalence property" written for all io-sim
functions that "guarantees" the semantics is consistent with what's provided by the IO
instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abailly-iohk that's a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created an issue for this: #137.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand traceTQueueDefault and if you say we cannot implement it correctly for the standard double TVar implementation, I need to trust you.
Just for the sake of explanation:
traceTVar
guarantees that its callback is called when the TVar
is committed in an stm transaction and receives the current (and previous) data stored in the TVar
. If TQueueDefault
is using two TVar
s io-sim
will need to know that not only the committed TVar
matters but also the other one. That's the reason why two TVar
s are problematic.
traceTVar
is designed for writing properties when order of events matters but one cannot relay on order of trace messages (i.e. in concurrent scenario order of trace messages will depend on the scheduler). traceTVar
callbacks are guaranteed to be called right after an atomic transaction is committed in this since they do not depend on the scheduler. If you do readTVarIO >>= traceM . show
, io-sim
will deschedule the thread after readTVarIO
and you're no longer guaranteed that multiple concurrent traceM
calls will be done in the same order as the atomic transactions.
29ed1a9
to
419679b
Compare
419679b
to
1938390
Compare
614974d
to
9e2444d
Compare
9e2444d
to
b1ff8fa
Compare
All comments addressed on this one @coot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Fixes #133 and also another issue found while fixing.
Now
flushTQueue
actually empties the queue and also maintains FIFO order.