Is AsyncReadExt::read_u64 cancel safe? - rust

In the documentation for AsyncReadExt::read_u64 it says it has the same errors as AsyncReadExt::read_exact, but says nothing about cancellation safety.
The same holds for all the other read_<type> functions on AsyncReadExt.
It seems likely that they have the same cancellation safety as read_exact (that is, none), but is that true?
Is there another way to read the next 4 bytes in a cancel safe way?
There's stuff in Tokio that covers my use case at a higher level, but I'd like to know how I would do this myself.

No it's not cancel safe
While the implementations of read_exact and the read_* functions differ they do the exact same thing:
Poll the underlying AsyncRead into a buffer, propagating errors appropriately.
If the reader returns Poll::Pending, propagate that.
If the buffer is full, return Ok(()).
If the buffer isn't full, repeat the whole thing over again.
If the future is canceled after some bytes are read it leaves the reader in an unknown state, thereby rendering them not cancel safe.
edit: making these methods object safe is difficult, the only way to do it is to rewrite the methods to do one of two things: when it is dropped, somehow communicate the internal state to a listener on the outside, probably via a channel, or have the future somehow run itself to completion when it's dropped. It would be preferrable to rewrite the surrounding code to not depend on its cancel safety.

Related

Get all avalable items from a futures Stream (non-blocking)

I have a WebSocket connection which wraps a futures_core::stream::Stream (incoming) and Sink (outgoing).
I want to decode and process all available messages from the Stream without blocking. Clearly at the socket level it's a TCP/IP stream of bytes and there is going to be 0..N messages sitting in the socket receive buffer waiting for a call to read(). A non-blocking call to read could well read multiple pipelined websocket frames. At the level of the Rust abstraction this might be possible with fn poll_next(...):
The trait is modelled after Future, but allows poll_next to be called
even after a value has been produced, yielding None once the stream
has been fully exhausted.
However, I don't know how to use this poll method directly without the async/await syntax, and even if I can, I don't see how it solves the problem. If I call it in a loop while I get back Some(frame), collecting the frames in a Vec, it will still suspend the task when it runs out of buffered frames and return Poll::Pending - so I won't be able to do anything with the collected frames immediately anyway. Ideally I need to process the collected frames when I get Poll::Pending without suspending anything, and then call it again allowing it to suspend only the second time around, if need be. Is there a solution possible here that doesn't involve discarding all of the future abstractions and resorting to buffering and parsing web socket frames myself?
You seem to have a misunderstanding of how suspensions work. When the parent function calls poll_next in a loop, it is not poll_next returning Poll::Pending that results in a suspension. Instead it is when the function containing the loop returns a Poll::Pending as a result of that. But there is nothing that says you have to do that immediatly. You are free to process the frames you have collected before returning to the executor.

Difference between cancel and uninterruptibleCancel (from the Async library)

Context:
I'm trying to understand the difference between cancel and uninterruptibleCancel from the Control.Concurent.Async package. I believe it has something to do with the underlying concepts of mask , uninterruptibleMask, and interruptible operations. Here's what I have understood so for:
Asynchronous exceptions are thrown by thread-A, but need to be handled by thread-B. This is precisely what throwTo does. In some way, this can also be considered as a form of inter-thread communication.
Asynchronous exceptions are used by one thread to kill/cancel another thread.
Handling aysnchronous exceptions creates a problem in the target/receiving thread, because one usually doesn't expect exceptions to be raised at any random point in the code. One puts try / catch around certain operations and expects/handles only certain exceptions. But, Asynchronous exceptions can be delivered when the target thread could be at any point in the execution.
mask allows use to protect critical sections in the target/receiving thread from delivery of asynchronous exceptions. The operation protected by mask doesn't need to deal with asynchronous-exceptions till the point it calls restore.
At this point uninterruptibleMask comes into the picture, and I start losing the plot. I thought the whole point of mask was to NOT deliver asynchronous-exceptions while executing a protected piece of code. However, here is what the docs say about "interruptible actions":
It is useful to think of mask not as a way to completely prevent asynchronous exceptions, but as a way to switch from asynchronous mode to polling mode. The main difficulty with asynchronous exceptions is that they normally can occur anywhere, but within a mask an asynchronous exception is only raised by operations that are interruptible (or call other interruptible operations). In many cases these operations may themselves raise exceptions, such as I/O errors, so the caller will usually be prepared to handle exceptions arising from the operation anyway. To perform an explicit poll for asynchronous exceptions inside mask, use allowInterrupt.
Questions:
Even within a code-block protected by mask, if there are some points where it is safe to handle asynchronous-exceptions, one can call allowInterrupt. This implicitly means, that, unless allowInterrupt is called, asynchronous exceptions will NOT be delivered while executing masked code. What, then, is the purpose of uninterruptibleMask?
Consequently, what is the need for uninterruptibleCancel? IIUC, thread A is trying to cancel thread B, but thread A, itself, is trying to protect itself from some sort of asynchronous exceptions, which may possibly be initiated by a third thread C, right? In the code for cancel (given below), which part is so critical that it needs the ultimate form of protection from asynchronous exceptions? Isn't throwTo an atomic/masked operation itself? Further, even if an asynchronous-exception is delivered to thread-A while executing waitCatch, what difference does it make? Actually, if I think about it, why do we need to even mask this code in the first place (let alone, uninterruptibleMask) ?
cancel a#(Async t _) = throwTo t AsyncCancelled <* waitCatch a
Under no masking, asynchronous exceptions can happen wherever. Under mask, asynchronous exceptions can only appear from interruptible actions (which are generally blocking). Under uninterruptibleMask, asynchronous exceptions are completely out of the picture. Also, please note that allowInterrupt is just one of the interruptible actions; there are a ton more, e.g. takeMVar. With just mask, it is e.g. impossible to block on an MVar without opening yourself up to exceptions, but uninterruptibleMask lets you do it (though you shouldn't).
uninterruptibleCancel is useful because cancel waits for the target thread to finish. This is a blocking operation, so, as is convention, it is also interruptible. Thus, when you use cancel, you open yourself up to receiving unexpected exceptions, whether you are masked or not. When you use uninterruptibleCancel, you are 100% guaranteed to not get an exception. That's it. Remember that exceptions are non-local; even if nothing in cancel is critical, leaving it unprotected means an exception can leak into something that is.
mask $ do
cancel something -- whoops, this can receive an exception, even though it's masked
someCleanup -- therefore this might not get called
vs.
mask $ do
uninterruptibleCancel something -- no exceptions
someCleanup -- so this will definitely happen (assuming the target thread ends)

Do AsyncIO stream writers/readers require manually ensuring that all data is sent/received?

When dealing with sockets, you need to make sure that all data is sent/received, since you may receive incomplete chunks of data when reading. From the docs:
In general, they return when the associated network buffers have been filled (send) or emptied (recv). They then tell you how many bytes they handled. It is your responsibility to call them again until your message has been completely dealt with.
Emphasis mine. It then shows sample implementations that ensure all data has been handled in each direction.
Is the same true though when dealing with AsyncIO wrappers over sockets?
For read, it seems to be required as the docs mention that it "[reads] up to n bytes.".
For write though, it seems like as long as you call drain afterwards, you know that it's all sent. The docs don't explicitly say that it must be called repeatedly, and write doesn't return anything.
Is this correct? Do I need to check how much was read using read, but can just drain the StreamWriter and know that everything was sent?
I thought that my above assumptions were correct, then I had a look at the example TCP Client immediately below the method docs:
import asyncio
async def tcp_echo_client(message):
reader, writer = await asyncio.open_connection(
'127.0.0.1', 8888)
print(f'Send: {message!r}')
writer.write(message.encode())
data = await reader.read(100)
print(f'Received: {data.decode()!r}')
print('Close the connection')
writer.close()
asyncio.run(tcp_echo_client('Hello World!'))
And it doesn't do any kind of checking. It assumes everything is both read and written the first time.
For read, [checking for incomplete read] seems to be required as the docs mention that it "[reads] up to n bytes.".
Correct, and this is a useful feature for many kinds of processing, as it allows you to read new data as it arrives from the peer and process it incrementally, without having to know how much to expect at any point. If you do know exactly how much you expect and need to read that amount of bytes, you can use readexactly.
For write though, it seems like as long as you call drain afterwards, you know that it's all sent. The docs don't explicitly say that it must be called repeatedly, and write doesn't return anything.
This is partially correct. Yes, asyncio will automatically keep writing the data you give it in the background until all is written, so you don't need to (nor can you) ensure it by checking the return value of write.
However, a sequence of stream.write(data); await stream.drain() will not pause the coroutine until all data has been transmitted to the OS. This is because drain doesn't wait for all data to be written, it only waits until it hits a "low watermark", trying to ensure (misguidedly according to some) that the buffer never becomes empty as long as there are new writes. As far as I know, in current asyncio there is no way to wait until all data has been sent - except for manually tweaking the watermarks, which is inconvenient and which the documentation warns against. The same applies to awaiting the return value of write() introduced in Python 3.8.
This is not as bad as it sounds simply because a successful write itself doesn't guarantee that the data was actually transmitted to, let alone received by the peer - it could be languishing in the socket buffer, or in network equipment along the way. But as long as you can rely on the system to send out the data you gave it as fast as possible, you don't really care whether some of it is in an asyncio buffer or in a kernel buffer. (But you still need to await drain() to ensure backpressure.)
The one time you do care is when you are about to exit the program or the event loop; in that case, a portion of the data being stuck in an asyncio buffer means that the peer will never see it. This is why, starting with 3.7, asyncio provides a wait_closed() method which you can await after calling close() to ensure that all the data has been sent. One could imagine a flush() method that does the same, but without having to actually close the socket (analogous to the method of the same name on file objects, and with equivalent semantics), but currently there are no plans to add it.

What are examples of types that implement only one of Send and Sync?

Just to get better understanding of the Send and Sync traits, are there examples of types that either:
Implement Send and do not implement Sync.
Implement Sync and do not implement Send.
First of all, it is important to realize that most structs (or enums) are Send:
any struct that does not contain any reference can be Send + 'static
any struct that contain references with a lower-bound lifetime of 'a can be Send + 'a
As a result, you would generally expect any Sync struct to be Send too, because Send is such an easy bar to reach (compared to the much harder bar of being Sync which requires safe concurrent modification from multiple threads).
However, nothing prevents the creator of a type to specifically mark it as not Send. For example, let's resuscitate conditions!
The idea of conditions, in Lisp, is that you setup a handler for a given condition (say: FileNotFound) and then when deep in the stack this condition is met then your handler is called.
How would you implement this in Rust?
Well, to preserve threads independence, you would use thread-local storage for the condition handlers (see std::thread_local!). Each condition would be a stack of condition handlers, with either only the top one invoked or an iterative process starting from the top one but reaching down until one succeeds.
But then, how would you set them?
Personally, I'd use RAII! I would bind the condition handler in the thread-local stack and register it in the frame (for example, using an intrusive doubly-linked list as the stack).
This way, when I am done, the condition handler automatically un-registers itself.
Of course, the system has to account for users doing unexpected things (like storing the condition handlers in the heap and not dropping them in the order they were created), and this is why we use a doubly-linked list, so that the handler can un-register itself from the middle of the stack if necessary.
So we have a:
struct ConditionHandler<T> {
handler: T,
prev: Option<*mut ConditionHandler<T>>,
next: Option<*mut ConditionHandler<T>>,
}
and the "real" handler is passed by the user as T.
Would this handler be Sync?
Possibly, depends how you create it but there is no reason you could not create a handler so that a reference to it could not be shared between multiple threads.
Note: those threads could not access its prev/next data members, which are private, and need not be Sync.
Would this handler be Send?
Unless specific care is taken, no.
The prev and next fields are not protected against concurrent accesses, and even worse if the handler were to be dropped while another thread had obtained a reference to it (for example, another handler trying to un-register itself) then this now dangling reference would cause Undefined Behavior.
Note: the latter issue means that just switching Option<*mut Handler<T>> for AtomicPtr<ConditionHandler<T>> is not sufficient; see Common Pitfalls in Writing Lock-Free Algorithms for more details.
And there you have it: a ConditionHandler<T> is Sync if T is Sync but will never be Send (as is).
For completeness, many types implement Send but not Sync (most Send types, actually): Option or Vec for example.
Cell and RefCell implement Send but not Sync because they can be safely sent between threads but not shared between them.

Design Pattern for multithreaded observers

In a digital signal acquisition system, often data is pushed into an observer in the system by one thread.
example from Wikipedia/Observer_pattern:
foreach (IObserver observer in observers)
observer.Update(message);
When e.g. a user action from e.g. a GUI-thread requires the data to stop flowing, you want to break the subject-observer connection, and even dispose of the observer alltogether.
One may argue: you should just stop the data source, and wait for a sentinel value to dispose of the connection. But that would incur more latency in the system.
Of course, if the data pumping thread has just asked for the address of the observer, it might find it's sending a message to a destroyed object.
Has someone created an 'official' Design Pattern countering this situation? Shouldn't they?
If you want to have the data source to always be on the safe side of concurrency, you should have at least one pointer that is always safe for him to use.
So the Observer object should have a lifetime that isn't ended before that of the data source.
This can be done by only adding Observers, but never removing them.
You could have each observer not do the core implementation itself, but have it delegate this task to an ObserverImpl object.
You lock access to this impl object. This is no big deal, it just means the GUI unsubscriber would be blocked for a little while in case the observer is busy using the ObserverImpl object. If GUI responsiveness would be an issue, you can use some kind of concurrent job-queue mechanism with an unsubscription job pushed onto it. ( like PostMessage in Windows )
When unsubscribing, you just substitute the core implementation for a dummy implementation. Again this operation should grab the lock. This would indeed introduce some waiting for the data source, but since it's just a [ lock - pointer swap - unlock ] you could say that this is fast enough for real-time applications.
If you want to avoid stacking Observer objects that just contain a dummy, you have to do some kind of bookkeeping, but this could boil down to something trivial like an object holding a pointer to the Observer object he needs from the list.
Optimization :
If you also keep the implementations ( the real one + the dummy ) alive as long as the Observer itself, you can do this without an actual lock, and use something like InterlockedExchangePointer to swap the pointers.
Worst case scenario : delegating call is going on while pointer is swapped --> no big deal all objects stay alive and delegating can continue. Next delegating call will be to new implementation object. ( Barring any new swaps of course )
You could send a message to all observers informing them the data source is terminating and let the observers remove themselves from the list.
In response to the comment, the implementation of the subject-observer pattern should allow for dynamic addition / removal of observers. In C#, the event system is a subject/observer pattern where observers are added using event += observer and removed using event -= observer.

Resources