Does WatchService.take() block other operations on WatchService? - watchservice

I'm wondering if it is possible to register more directories or close a WatchService after take() has been called in a different thread. Will a ClosedWatchServiceException then be thrown in the thread that called take()? Or will the other operations not have effect until a change event happens and take() returns? Thanks

Related

Why is HANDLE event object assumed valid in thread function?

Why is HANDLE event object(synchronization object which is created by CreateEvent function) in winapi assumed to be valid in thread function?
From multithreading example to microsoft docs code examples, this event object is passed to WaitForSingleObject function without any protection.
I've been doing the same. And today, I just reached to the thought that how can I deal with this "branch" safe, in such a sense like branch coverage in code perspective.
In the strict sense, this event object is shared along multiple threads, at least in the thread which calls SetEvent and in the thread which Calls WaitForSingleObject.
Therefore, it has to be classified as a type of shared resource. Then, all shared resources must be protected by "lock", such as mutex or critical section.
Also, it is possible to deliberately call CloseHandle after SetEvent while thread is alive, which will lead to passing closed event handle to WaitForSingleObject in thread function. (maybe the event object won't be deleted due to deferred deletion)
Acquiring lock and calling WaitForSingleObject in thread function, and trying to acquire lock in other thread in order to call SetEvent would definitely lead to deadlock.
[EDIT]
Maybe I misled my point by mentioning "assumed" and particular code example. I wonder how to do thread safe validity check for HANDLE event object, treating HANDLE as variable.
According to Synchronizing Execution of Multiple Threads, There are a number of objects whose handles can be used to synchronize multiple threads. These objects include:
Console input buffers
Events
Mutexes
Processes
Semaphores
Threads
Timers
The state of each of these objects is either signaled or not signaled.(atomic)
For handle concerned, WaitForSingleObject function say If this handle is closed while the wait is still pending, the function's behavior is undefined.
For an invalid handle, It's programmer's responsibility to troubleshoot where the handle becomes invalid(BUG).

In a thread which never calls asio::io_conterxt.run(), must I invoke post() to dispatch tasks to the thread which has called io_context.run()?

In a thread which has not ever called and would never call asio::io_context.run(), must I invoke post() or dispatch() to dispatch tasks to the thread which has called asio::io_context.run()?
Is it safe to directly call asio::async_write() or asio::async_read() in a thread which has not ever called and would never call asio::io_context.run() to dispatch tasks to the thread which has called asio::io_context.run()?
In a thread which has not ever called and would never call asio::io_context.run(), must I invoke post() or dispatch() to dispatch tasks to the thread which has called asio::io_context.run()?
That's basically how that works. Yes, it's also a "cheap" way to implement a task queue (see e.g. stackoverflow.com/questions/…)
To the first part: yes. (You can replace "must" with "can")
Is it safe to directly call asio::async_write() or asio::async_read() in a thread which has not ever called and would never call asio::io_context.run() to dispatch tasks to the thread which has called asio::io_context.run()?
Yes (with caveats).
The caveats are that you're responsible for thread safety/synchronization. E.g. a tcp::socket object is not thread-safe. You should only call methods on it from one logical thread (e.g. strand) or critical section (e.g. using mutual exclusions, mutex).
The async initiators will work to get work onto the execution context (which .run()s on any number of threads). From there it is highly idiomatic that all subsequent async initiation functions happen from completion handlers, so from these threads already.
Note that none of this is magical. In fact, all the async_ initiation functions know the executor (associated with the IO object, usually) and this determines where the completion handler gets post/dispatch/defer-ed to. In some cases you want to override this (e.g. using strand.wrap() or the newer bind_executor() function).
See also When must you pass io_context to boost::asio::spawn? (C++)

How does std::notify_all_at_thread_exit work?

According to cppref:
std::notify_all_at_thread_exit provides a mechanism to notify other
threads that a given thread has completely finished, including
destroying all thread_local objects.
I know the exact semantics of std::notify_all_at_thread_exit. What makes me puzzled is:
How to register a callback function that will be called after a given thread has finished and destroyed all of its thread-local objects?
std::notify_all_at_thread_exit takes a condition variable in its first parameter, by reference. When the thread exits, it will call notify_all on that condition variable, waking up threads that are waiting for the condition variable to be notified.
There doesn't appear to be a direct way to truly register a callback for this; you'll likely need to have a thread waiting for the condition variable to be notified (using the same lock as the one passed into std::notify_all_at_thread_exit. When the CV is notified, the thread that's waiting should verify that the wakeup isn't spurious, and then execute the desired code that should be run.
More info about how this is implemented:
At least on Google's libcxx, std::notify_all_at_thread_exit calls __thread_struct_imp::notify_all_at_thread_exit, which stores a pair with the parameters to a vector (_Notify). Upon thread death, the destructor of __thread_struct_imp iterates over this vector and notifies all of the condition variables that have been registered in this way.
Meanwhile, GNU stdc++ uses a similar approach: A notifier object is created, it's registered with __at_thread_exit, it's designed to call its destructor when run at thread exit, and the destructor actually performs the notification process. I'd need to investigate __at_thread_exit more closely as I don't understand its inner workings fully just yet.

What does inside a strand mean?

I'm currently trying to get my hands on boost::asio strands. Doing so, I keep reading about "invoking strand post/dispatch inside or outside a strand". Somehow I can't figure out how inside a strand differs from through a strand, and therefore can't grasp the concept of invoking a strand function outside the strand at all.
Probably there is just a small piece missing in my puzzle. Can somebody please give an example how calls to a strand can be inside or outside it?
What I think I've understood so far is that posting something through a strand would be
m_strand.post(myfunctor);
or
m_strand.wrap(myfunctor);
io_svc.post(myfunctor);
Is the latter considered a call to dispatch outside the strand (as opposed to the other being a call to post inside it)? Is there some relation between the strand's "inside realm" and the threads the strand operates on?
If being inside a strand simply meant to invoke a strand's function, then the strand class's documentation would be pointless. It states that strand::post can be invoked outside the strand... That's precisely the part I don't understand.
Even I had some trouble in understanding this concept, but became clear once I started working on libdispatch. It helped me map things with asio better.
Now lets see how to make some sense out of strand. Consider strand as a serial queue of handlers which needs to be executed.
Now, where does these handlers get executed ? Within the worker threads.
Where did these worker threads come from ? From the io_service object you passed while creating the strand.
Something like:
asio::strand s(io_serv_obj);
Now, as you must be knowing, the io_service::run can be called by a single thread or multiple threads. The threads calling the run method of the io_serv_obj are the worker threads for that strand in our case. So, it could be either single threaded or multithreaded.
Coming back to strands, when you post a handler, that handler is always enqueued in the serial queue which we talked about. The worker threads will pick up the handler from the queue one after the other.
Now, when you do a dispatch, asio does some optimization for you:
It checks whether you are calling it from inside one of the worker thread or from some other thread (maybe of some other io_service instance). When it is called outside the current execution context of the strand, thats when it is called outside the strand. So, in the outside case, the dispatch will just enqueue the handler like post when there are other handlers waiting in the queue or will call it directly when it can guarantee that it will not be called concurrently with any other handler from that queue that may be running in one of the worker threads at that moment.
UPDATE:
As noted in the comments section, inside means called within another handler i.e for eg: I posted a handler A and inside that handler, I am doing a dispatch of another handler. Now, as would be explained in #2, if there are no other handlers waiting in the strands serial queue, the dispatch handler will be called synchronously. If this condition is not met, that means, the dispatch is called from outside.
Now, if you call dispatch from outside of the strand i.e not within the current execution context, asio checks its callstack to see if any other handler present in its serial queue is running or not. If not, then it will directly call that handler synchronously. So, there is no cost of enqueueing the handler (I think no extra allocation will be done as well, not sure though).
Lets see the documentation link now:
s.dispatch(a) happens-before s.post(b), where the former is performed
outside the strand
This means that, if dispatch was called from some outside the current run OR there are other handlers already enqueued, then it needs to enqueue the handler, it just cannot call it synchronously. Since its a serial queue, a will get executed before b.
Had there been another call s.dispatch(c) along with a and b but before a and b(in the mentioned order) enqueued, then c will get executed before a and b, but in no way b can get executed before a.
Hope this clears your doubt.
For a given strand object s, running outside s implies that s.running_in_this_thread() returns false. This returns true if the calling thread is executing a handler that was submitted to the strand via post(), dispatch(), or wrap(). Otherwise, it returns false:
io_service.post(handler); // handler will run outside of strand
strand.post(handler); // handler will run inside of strand
strand.dispatch(handler); // handler will run inside of strand
io_service.post(strand.wrap(handler)); // handler will run inside of strand
Given:
a strand object s
a function object f1 that is added to strand s via s.post(), or s.dispatch() when s.running_in_this_thread() == false
a function object f2 that is added to strand s via s.post(), or s.dispatch() when s.running_in_this_thread() == false
then the strand provides a guarantee of ordering and non-concurrency, such that f1 and f2 will not be invoked concurrently. Furthermore, if the addition of f1 happens before the addition of f2, then f1 will be invoked before f2.

What happens to std::async call if parent/main thread dies

If I am right, the std::async uses a new thread and calls the method in it. I was wondering what happens if the main thread or the parent thread dies. Does the thread controlling the async method dies as well.
There is no concept of a "parent" thread in C++, each thread is independent of the one that it was created by. However, the main thread is special and if it returns from main() or calls exit() then the entire application is terminated even if other threads are still running. Once that happens, the program has undefined behaviour if the still-running threads access any global variables or automatic objects that were on the main thread's stack, or use any standard library objects or call any function not permitted in signal handlers.
In short, do not let other threads run after main completes if you expect sensible results.