C ++: How to call a synchronous library call asynchronously?

I am working with a library that has a blocking call that never expires if it fails. I would like to handle this error condition more gracefully. I know that there should be a way to wrap the call in a workflow (or some other delegate object), wait x number of seconds, and then throw an exception if x seconds have passed. I need to do this for only one function in the library. How can i implement this? I see similar examples all over the network, but none of them do exactly what I am trying to do. Thanks!

+4
source share
5 answers

My answer is: "Do not try to do this."

Of course, you are likely to find some kind of hack that seems to work in your particular case. But the race conditions here are very difficult to fix.

An obvious approach is to let thread A make a blocking call, and then set thread B to kill A if the timeout expires.

But ... What if the timeout expires while A returning from the blocking call? In particular, what if B thinks it's time to kill A , then the OS scheduler decides to start A for a while, then your OS decides to run code B that kills A ?

Bottom line: you end killing A at some vague point in its execution. (For example, perhaps he simply deducted $ 500 from a savings account, but has not yet added $ 500 to his current account. The possibilities are endless ...)

OK, so you can have thread A for the sole purpose of starting a library call, and then signal a condition or something else when it ends. At least this work can be done in principle. But even then, what if the library itself has some kind of internal state that remains in an inconsistent state, should A be killed at the wrong time?

There are good reasons why asynchronous thread cancellation was excluded from the C ++ 11 standard. Just say no. Correct the library procedure. No matter what it costs, it will almost certainly be cheaper in the long run than what you are trying.

+2
source

Using C ++ 11, then starting the thread explicitly for this call might look like this:

 // API T call(int param); // asynchronous call with 42 as parameter auto future = std::async(std::launch::async, call, 42); // let wait for 40 ms auto constexpr duration = std::chrono::milliseconds(40); if(future.wait_for(duration) == std::future_status::timeout) { // We waited for 40ms and had a timeout, now what? } else { // Okay, result is available through future.get() // if call(42) threw an exception then future.get() will // rethrow that exception so it worth doing even if T is void future.get(); } 

As you can see in the case of a timeout, you have a big problem, since you are permanently stuck in a blocked stream. This may not be a C ++ 11 std::future bug: a fair number of thread abstractions will provide the best possible undo, and this will still not be enough to save you.

If you are not using C ++ 11, then Boost.Thread has a very similar interface with boost::unique_future (where wait_for instead of timed_wait returns bool ), although it does not have something similar to std::async , so you need to do the work (for example, boost::packaged_task + boost::thread ). Available details in the documentation .

+2
source

Obviously, the thread in which the blocking call is made cannot kill itself - it will be blocked.

One approach is to start thread A, which makes a blocking call, and then start another thread B, which sleeps for a timeout, and then kill thread A. A protected common flag marked with a mutex can indicate whether the operation Successful, based on which an exception can be thrown or not.

The second approach (very similar) would be to start thread A, which in turn starts thread B, sleeps for a timeout, and then kills thread B.

The specifics of your preferred thread library (for example, which threads allow you to kill each other) and the nature of the lock function will exactly affect how you do it.

+1
source

On Windows, you'll want to do something like this:

 //your main thread DWORD threadID; HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadProc, 0, 0, &threadID); DWORD ret = 0xFFFFFF; for (int i = 0; i < /*some timeout*/; i++) { ret = WaitForSingleObject(h, 100); if (ret == WAIT_OBJECT_0) break; } if (ret != WAIT_OBJECT_0) { DWORD exitCode; TerminateThread(h, &exitCode); // you will want to stop the thread as it isn't exiting. /*throw*/; } 

and

 //Thread Routine DWORD ThreadProc (LPVOID threadParam) { //call your function here return 0; } 

The idea here is to deploy a thread to do the job. Then you can wait for this stream in 100 ms (or whatever). If this does not end within a certain period of time, you can throw an exception.

0
source

There are some problems. First, does the library support any internal state that will not be available if the library is not called successfully? If so, you are stuft because calls following a failed call that is being blocked also fail or, even worse, generate erroneous results without any exception or other notification.

If the library is safe, you can try to disconnect the call and wait for some timeout event. Now you need to solve @Nemo's problems - you need to take care of how you handle returning results. How exactly you do this depends on how you intend to return the results from the stream that calls the library. Typically, both threads will go into the critical section for safe arbitrage between the results of returning the lib stream and the timeout stream instructing the lib stream to never return anything (for example, by setting a flag to it) and just exit if the lib call ever coming back.

Liberation of the library. thread this way will leak the thread if the lib call never returns. Regardless of whether you can absorb such leaks or safely resort to the possible forced termination of orphaned flows, is between you and your application :)

0
source

Source: https://habr.com/ru/post/1402485/


All Articles