Potential kind of asynchronous (overlapped) I/O implementation in Windows - windows

I would like to discuss potential kind of asynchronous (Overlapped) I/O implementations in Windows, because there are many ways to implement this.
Overlapped I/O in Windows provides the ability to process data asynchronously, ie the execution of the operations are nonblocking.
Edit: The purpose of this question is the discussion about improvement of my own implementation on the one hand, and the discussion of alternate implementation on the other hand. What asynchronous I/O implementation would make most sense on parallel heavy I/O, what make most sense in small mostly single threaded application.
I will cite MSDN:
When a function is executed synchronously, it does not return until the operation has been completed. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to finish. Functions called for overlapped operation can return immediately, even though the operation has not been completed. This enables a time-consuming I/O operation to be executed in the background while the calling thread is free to perform other tasks. For example, a single thread can perform simultaneous I/O operations on different handles, or even simultaneous read and write operations on the same handle.
I assume that the reader is familiar with the basic concept of overlapped I/O.
Another solution for asynchronous I/O are completions ports, but this shall not be the subject of this discussion. More information on other I/O concepts can be found on MSDN "About File Management > Input and Output (I/O) > I/O Concepts"
I would like to present my (C/C++) implementation here and share it for discussion.
This is my extended OVERLAPPED struct called IoOperation:
struct IoOperation : OVERLAPPED {
HANDLE Handle;
unsigned int Operation;
char* Buffer;
unsigned int BufferSize;
}
This struct is created each time an asynchronous operation like ReadFile or WriteFile is called. The Handle field shall be initialized with the corresponding device/file handle. Operation is a user defined field that tells what operation was called. The field Buffer is a pointer to a previously allocated chunk of memory with the given size BufferSize. Of course, this struct can be expanded at will. It could contain the operation result, acutaully transfered size etc.
The first thing we need is an (auto reset) event handle to be signaled each time an overlapped I/O is completed.
HANDLE hEvent = CreateEvent(0, FALSE, FALSE, 0);
First I decided to use only one event for all asynchronous operations. Then I decided to register this event with a thread pool thread with RegisterWaitForSingleObject.
HANDLE hWait = 0;
....
RegisterWaitForSingleObject(
&hWait,
hEvent,
WaitOrTimerCallback,
this,
INFINITE,
WT_EXECUTEINPERSISTENTTHREAD | WT_EXECUTELONGFUNCTION
);
So each time this event is signaled, my callback WaitOrTimerCallback is called.
An asynchronous operation is initialized like this:
IoOperation* Io = new IoOperation(hFile, hEvent, IoOperation::Write, Data, DataSize);
if (IoQueue->Enqueue(Io)) {
WriteFile(hFile, Io->Buffer, Io->BufferSize, 0, Io);
}
Each operation is queued and is removed after successful GetOverlappedResult call in my WaitOrTimerCallback callback. Instead calling new all the time here, we could use a memory pool to avoid memory fragmentation and to make allocation faster.
VOID CALLBACK WaitOrTimerCallback(PVOID Parameter, BOOLEAN TimerOrWaitFired) {
list<IoOperation*>::iterator it = IoQueue.begin();
while (it != IoQueue.end()) {
bool IsComplete = true;
DWORD Transfered = 0;
IoOperation* Io = *it;
if (GetOverlappedResult(Io->Handle, Io, &Transfered, FALSE)) {
if (Io->Operation == IoOperation::Read) {
// Handle Read, virtual OnRead(), SetEvent, etc.
} else if (Io->Operation == IoOperation::Write) {
// Handle Read, virtual OnWrite(), SetEvent, etc.
} else {
// ...
}
} else {
if (GetLastError() == ERROR_IO_INCOMPLETE) {
IsComplete = false;
} else {
// Handle Error
}
}
if (IsComplete) {
delete Io;
it = IoQueue.erase(it);
} else {
it++;
}
}
}
Of course, to be multi threading safe, we need a lock protection (critical section) when accessing the I/O queue for example.
There are advantages but also disadvantage of this kind of implementation.
Advantages:
Execution in persistent thread pool thread, no manual thread creation is required
Only one event is required
Each operation is queued in an I/O queue (CancelIoEx can be called later)
Disadvantages:
I/O queue requires extra memory/cpu time
GetOverlappedResult is called for all queued I/O's even incompleted ones

Related

Boost stackful coroutine for websocket, how to post a function and resume to do from a another thread

int main()
{
tcp::socket socket(iocp);
acceptor.async_accept(socket, yield[ec]);
if (ec)
fail(ec, "accept");
else
boost::asio::spawn(acceptor.get_executor(), std::bind(&do_session, websocket::stream<beast::tcp_stream>(std::move(socket)), std::placeholders::_1));
... iocp run
}
void do_session(websocket::stream<beast::tcp_stream>& ws, net::yield_context yield)
{
while(ws.is_open())
{
ws.async_read(buffer, yield[ec]);
... process the buffer
... execute posted callbacks
}
}
void another_thread()
{
while(isAppNotExit)
{
post_to_specified_coroutine(ws, []() { ... do in courutine same thread });
}
}
I need to post a function in any thread to let the specified coroutine run the function, that is the code part of "execute posted callbacks" above. However, after this task is delivered, the coroutine may be in async_read or async_write state. Is it possible to post an event like data and let the async_read or async_write function return immediately?
I guess the essence of the problem is this: use select on 2 channels: a channel with capacity=1 and a channel with (possibly) infinite capacity.
Implement select with asio asynchronous operation
Write an asio asynchronous operation to wait for multiple (two) things.
(asio asynchronous operation template: c++ - How to wait for a function to return with Boost:::Asio? - Stack Overflow).
state protected by a mutex:
a std::optional<read_result>
a std::vector<functor>
a bool (whether there is an on-going async_read)
a std::optional<completion handler>
your async_wait_for_2_things:
Get the completion handler (a callable, can resume your coroutine) from the completion token (yield[ec]);
Lock the mutex (use guard);
if there is a pending functor from another_thread, take it out, post the completion handler;
else if there is a pending read_result, take it out, post the completion handler;
else if there is a an on-going async_read (the bool is true), store the completion handler (if there is already a completion handler stored, throw "can not happen");
else (no pending functor, no pending read_result, async_read has not been started), store the completion handler (if there is already a completion handler stored, throw "can not happen"), set the bool to true (if the bool is already true, throw "can not happen"), call async_read;
Unlock the mutex;
async_read's callback:
Lock the mutex (use guard);
set the bool to false (if the bool is already false, throw "can not happen");
if there is a completion handler, take it out, post it;
else, store read_result (if there is already a read_result stored, throw "can not happen");
Unlock the mutex;
another_thread's code for posting functor:
Lock the mutex (use guard);
if there is a completion handler, take it out, post it;
else, store functor;
Unlock the mutex;
Implement select using asynchronous event
async_read(use callback overload)'s lambda completion handler: stores result, notifies asynchronous_event;
another_thread: stores functor, notifies asynchronous_event;
do_session: asynchronously waits on asynchronous_event, loads result or functor;
asynchronous_event's data is in a std::pair<std::optional<read_result>, std::vector<functor>> protected by a mutex;
Implement asynchronous event using a timer: c++ - Why does Boost.Asio not support an event-based interface? - Stack Overflow.
This is not applicable because "asynchronous event" is-not-a "asynchronous condition variable", it can not:
release a mutex and block in asynchronous wait atomically
(a possible sequence: do_session release mutex, then functor is posted, then event is notified (cancel_one), then do_session wait on event (timer_.async_wait(yield[ec]);) and blocks forever)
Implement select using asynchronous latch
async_read(use callback overload)'s lambda handler: ①stores result and resets asynchronous_latch_producer, ②notifies asynchronous_latch_consumer, waits on asynchronous_latch_producer(, ⑥wake up);
another_thread: ①stores functor and resets asynchronous_latch_producer, ②notifies asynchronous_latch_consumer, waits on asynchronous_latch_producer(, ⑥wake up);
do_session: waits on asynchronous_latch_consumer(, ③wake up), ④loads result or functor and resets asynchronous_latch_consumer, ⑤notifies asynchronous_latch_producer;
asynchronous_latch_consumer and asynchronous_latch_producer's data is in a std::pair<std::optional<read_result>, std::vector<functor>>;
Implement asynchronous latch using a timer: c++ - Cancelling boost asio deadline timer safely - Stack Overflow. Modify that asynchronous event implementation to get asynchronous latch: in constructor and reset, .expires_at(Timer::clock_type::time_point::max()); in notify_all_one_shot, .expires_at(Timer::clock_type::time_point::min()).
This is not applicable because one of the producer might block forever.

How can an interprocess producer consumer message passing mechanism be protected against corruption due to one side crashing?

I have implemented an interprocess message queue in shared memory for one producer and one consumer on Windows.
I am using one named semaphore to count empty slots, one named semaphore to count full slots and one named mutex to protect the data structure in shared memory.
Consider, for example the consumer side. The producer side is similar.
First it waits on the full semaphore then (1) it takes a message from the queue under the mutex and then it signals the empty semaphore (2)
The problem:
If the consumer process crashes between (1) and (2) then effectively the number of slots in the queue that can be used by the process is reduced by one.
Assume that while the consumer is down, the producer can handle the queue getting filled up. (it can either specify a timeout when waiting on the empty semaphore or even specify 0 for no wait).
When the consumer restarts it can continue to read data from the queue. Data will not have been overrun but even after it empties all full slots, the producer will have one less empty slot to use.
After multiple such restarts the queue will have no slots that can be used and no messages can be sent.
Question:
How can this situation be avoided or recovered from?
Here's an outline of one simple approach, using events rather than semaphores:
DWORD increment_offset(DWORD offset)
{
offset++;
if (offset == QUEUE_LENGTH*2) offset = 0;
return offset;
}
void consumer(void)
{
for (;;)
{
DWORD current_write_offset = InterlockedCompareExchange(write_offset, 0, 0);
if ((current_write_offset != *read_offset + QUEUE_LENGTH) &&
(current_write_offset + QUEUE_LENGTH != *read_offset))
{
// Queue is not full, make sure producer is awake
SetEvent(signal_producer_event);
}
if (*read_offset == current_write_offset)
{
// Queue is empty, wait for producer to add a message
WaitForSingleObject(signal_consumer_event, INFINITE);
continue;
}
MemoryBarrier();
_ReadWriteBarrier;
consume((*read_offset) % QUEUE_LENGTH);
InterlockedExchange(read_offset, increment_offset(*read_offset));
}
}
void producer(void)
{
for (;;)
{
DWORD current_read_offset = InterlockedCompareExchange(read_offset, 0, 0);
if (current_read_offset != *write_offset)
{
// Queue is not empty, make sure consumer is awake
SetEvent(signal_consumer_event);
}
if ((*write_offset == current_read_offset + QUEUE_LENGTH) ||
(*write_offset + QUEUE_LENGTH == current_read_offset))
{
// Queue is full, wait for consumer to remove a message
WaitForSingleObject(signal_producer_event, INFINITE);
continue;
}
produce((*write_offset) % QUEUE_LENGTH);
MemoryBarrier();
_ReadWriteBarrier;
InterlockedExchange(write_offset, increment_offset(*write_offset));
}
}
Notes:
The code as posted compiles (given the appropriate declarations) but I have not otherwise tested it.
read_offset is a pointer to a DWORD in shared memory, indicating which slot should be read from next. Similarly, write_offset points to a DWORD in shared memory indicating which slot should be written to next.
An offset of QUEUE_LENGTH + x refers to the same slot as an offset of x so as to disambiguate between a full queue and an empty queue. That's why the increment_offset() function checks for QUEUE_LENGTH*2 rather than just QUEUE_LENGTH and why we take the modulo when calling the consume() and produce() functions. (One alternative to this approach would be to modify the producer to never use the last available slot, but that wastes a slot.)
signal_consumer_event and signal_producer_event must be automatic-reset events. Note that setting an event that is already set is a no-op.
The consumer only waits on its event if the queue is actually empty, and the producer only waits on its event if the queue is actually full.
When either process is woken, it must recheck the state of the queue, because there is a race condition that can lead to a spurious wakeup.
Because I use interlocked operations, and because only one process at a time is using any particular slot, there is no need for a mutex. I've included memory barriers to ensure that the changes the producer writes to a slot will be seen by the consumer. If you're not comfortable with lock-free code, you'll find that it is trivial to convert the algorithm shown to use a mutex instead.
Note that InterlockedCompareExchange(pointer, 0, 0); looks a bit complicated but is just a thread-safe equivalent to *pointer, i.e., it reads the value at the pointer. Similarly, InterlockedExchange(pointer, value); is the same as *pointer = value; but thread-safe. Depending on the compiler and target architecture, interlocked operations may not be strictly necessary, but the performance impact is negligible so I recommend programming defensively.
Consider the case when the consumer crashes during (or before) the call to the consume() function. When the consumer is restarted, it will pick up the same message again and process it as normal. As far as the producer is concerned, nothing unusual has happened, except that the message took longer than usual to be processed. An analogous situation occurs if the producer crashes while creating a message; when restarted, the first message generated will overwrite the incomplete one, and the consumer won't be affected.
Obviously, if the crash occurs after the call to InterlockedExchange but before the call to SetEvent in either the producer or consumer, and if the queue was previously empty or full respectively, then the other process will not be woken up at that point. However, it will be woken up as soon as the crashed process is restarted. You cannot lose slots in the queue, and the processes cannot deadlock.
I think the simple multiple-producer single-consumer case would look something like this:
void producer(void)
{
for (;;)
{
DWORD current_read_offset = InterlockedCompareExchange(read_offset, 0, 0);
if (current_read_offset != *write_offset)
{
// Queue is not empty, make sure consumer is awake
SetEvent(signal_consumer_event);
}
produce_in_local_cache();
claim_mutex();
// read offset may have changed, re-read it
current_read_offset = InterlockedCompareExchange(read_offset, 0, 0);
if ((*write_offset == current_read_offset + QUEUE_LENGTH) ||
(*write_offset + QUEUE_LENGTH == current_read_offset))
{
// Queue is full, wait for consumer to remove a message
WaitForSingleObject(signal_producer_event, INFINITE);
continue;
}
copy_from_local_cache_to_shared_memory((*write_offset) % QUEUE_LENGTH);
MemoryBarrier();
_ReadWriteBarrier;
InterlockedExchange(write_offset, increment_offset(*write_offset));
release_mutex();
}
}
If the active producer crashes, the mutex will be detected as abandoned; you can treat this case as if the mutex were properly released. If the crashed process got as far as incrementing the write offset, the entry it added will be processed as usual; if not, it will be overwritten by whichever producer next claims the mutex. In neither case is any special action needed.

triggering user space with kernel

I need to send a string from kernel to a user space function without asking for it in particular from the user space, sort of triggering a function or application in the user space via some event in kernel.
So far I have tried an Ioctl that starts on an init in user space and then sleeps and kept reading about netlink but couldn't find a good working example for it.
Any suggestions or examples will be much obliged.
Here's how my process works, I would be interested in any suggestions for improvements as well:
Start the kernel module
Start user space application, which sends a custom command to the kernel module to register the user space PID for kernel module signals. In my case this was via a write to /dev/mymodule. The kernel module registers the PID:
...
printk("registering a new process id to receive signals: %d\n", current->pid);
signal_pid = current->pid;
...
The user space application also registers a handler with the kernel for certain types of signals.
void local_sig_handler(int signum) {
printf("received a signal from my module\n");
fflush(stdout); }
...
signal(SIGIO, local_sig_handler);
Kernel module generates a signal
...
struct siginfo info;
struct task_struct *t;
info.si_signo=SIGIO;
info.si_int=1;
info.si_code = SI_QUEUE;
printk("<1>IRQ received: %d\n", irq);
printk("<1>searching for task id: %d\n", signal_pid);
t= pid_task(find_vpid(signal_pid),PIDTYPE_PID);//user_pid has been fetched successfully
if(t == NULL){
printk("<1>no such pid, cannot send signal\n");
} else {
printk("<1>found the task, sending signal\n");
send_sig_info(SIGIO, &info, t);
}
Kernel relays the signal to the application's handler
You've got a few options:
Signals. User process defines a signal handler, and kernel signals the user process upon receipt of an event. This works well, but requires that the handling code run in an async signal handler (which makes it trickier to write correct code). The downside is that the amount of data you can transmit using a signal handler is somewhat limited. Make sure to use a signal that can be queued (e.g. a realtime signal) so you don't lose messages when the process is in the middle of handling a signal.
Blocking system call or file access. User process executes a system call (or reads/writes a file) which puts it to sleep. The kernel driver for the call maintains a queue of events, and dequeues events when they arrive and when a blocked waiter exists (this avoids losing events when the user process is unblocked).
Create a system call which configures a sigevent. On the kernel side, create a sigqueue to fire the relevant events.
An example I used in the past was to send signal to user space from hardware interrupt in kernel space.
KERNEL SPACE
You have to prepare siginfo and task_struct before sending a signal:
struct siginfo info;
struct task_struct *t;
info.si_signo = SIG_TEST;
info.si_code = SI_QUEUE;
info.si_int = 1234; // Any value you want to send
rcu_read_lock();
And also find the task with user space application PID. You have to send it from user-space to kernel-space through write or ioctl operations.
t = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
Then you can send the signal.
rcu_read_unlock();
send_sig_info(SIG_TEST, &info, t);
I omitted here, but you must check the result of every operation.
The previous code prepare the signal structure and send it. Bear in mind that you need the application's PID. In my case the application from user space send its PID through ioctl driver procedure.
USER SPACE
You have to define and implement the callback function:
void signalFunction(int n, siginfo_t *info, void *unused) {
.....
.....
}
In main procedure:
struct sigaction sig;
sig.sa_sigaction = signalFunction; // Callback function
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig, NULL);
I hope it helps.

How to detect WinSock TCP timeout with BindIoCompletionCallback

I am building a Visual C++ WinSock TCP server using BindIoCompletionCallback, it works fine receiving and sending data, but I can't find a good way to detect timeout: SetSockOpt/SO_RCVTIMEO/SO_SNDTIMEO has no effect on nonblocking sockets, if the peer is not sending any data, the CompletionRoutine is not called at all.
I am thinking about using RegisterWaitForSingleObject with the hEvent field of OVERLAPPED, that might work but then CompletionRoutine is not needed at all, am I still using IOCP ? is there a performance concern if I use only RegisterWaitForSingleObject and not using BindIoCompletionCallback ?
Update: Code Sample:
My first try:
bool CServer::Startup() {
SOCKET ServerSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
WSAEVENT ServerEvent = WSACreateEvent();
WSAEventSelect(ServerSocket, ServerEvent, FD_ACCEPT);
......
bind(ServerSocket......);
listen(ServerSocket......);
_beginthread(ListeningThread, 128 * 1024, (void*) this);
......
......
}
void __cdecl CServer::ListeningThread( void* param ) // static
{
CServer* server = (CServer*) param;
while (true) {
if (WSAWaitForMultipleEvents(1, &server->ServerEvent, FALSE, 100, FALSE) == WSA_WAIT_EVENT_0) {
WSANETWORKEVENTS events = {};
if (WSAEnumNetworkEvents(server->ServerSocket, server->ServerEvent, &events) != SOCKET_ERROR) {
if ((events.lNetworkEvents & FD_ACCEPT) && (events.iErrorCode[FD_ACCEPT_BIT] == 0)) {
SOCKET socket = accept(server->ServerSocket, NULL, NULL);
if (socket != SOCKET_ERROR) {
BindIoCompletionCallback((HANDLE) socket, CompletionRoutine, 0);
......
}
}
}
}
}
}
VOID CALLBACK CServer::CompletionRoutine( __in DWORD dwErrorCode, __in DWORD dwNumberOfBytesTransfered, __in LPOVERLAPPED lpOverlapped ) // static
{
......
BOOL res = GetOverlappedResult(......, TRUE);
......
}
class CIoOperation {
public:
OVERLAPPED Overlapped;
......
......
};
bool CServer::Receive(SOCKET socket, PBYTE buffer, DWORD length, void* context)
{
if (connection != NULL) {
CIoOperation* io = new CIoOperation();
WSABUF buf = {length, (PCHAR) buffer};
DWORD flags = 0;
if ((WSARecv(Socket, &buf, 1, NULL, &flags, &io->Overlapped, NULL) != 0) && (GetLastError() != WSA_IO_PENDING)) {
delete io;
return false;
} else return true;
}
return false;
}
As I said, it works fine if the client is actually sending data to me, 'Receive' is not blocking, CompletionRoutine got called, data received, but here is one gotcha, if the client is not sending any data to me, how can I give up after a timeout ?
Since SetSockOpt/SO_RCVTIMEO/SO_SNDTIMEO wont help here, I think I should use the hEvent field in the OVERLAPPED stucture which will be signaled when the IO completes, but a WaitForSingleObject / WSAWaitForMultipleEvents on that will block the Receive call, and I want the Receive to always return immediately, so I used RegisterWaitForSingleObject and WAITORTIMERCALLBACK. it worked, the callback got called after the timeout, or, the IO completes, but now I have two callbacks for any single IO operation, the CompletionRoutine, and the WaitOrTimerCallback:
if the IO completed, they will be called simutaneously, if the IO is not completed, WaitOrTimerCallback will be called, then I call CancelIoEx, this caused the CompletionRoutine to be called with some ABORTED error, but here is a race condition, maybe the IO will be completed right before I cancel it, then ... blahblah, all in all its quite complicated.
Then I realized I dont actually need BindIoCompletionCallback and CompletionRoutine at all, and do everything from the WaitOrTimerCallback, it may work, but here is the interesting question, I wanted to build an IOCP-based Winsock server in the first place, and thought BindIoCompletionCallback is the easiest way to do that, using the threadpool provied by Windows itself, now I endup with a server without IOCP code at all ? is it still IOCP ? or should I forget BindIoCompletionCallback and build my own IOCP threadpool implementation ? why ?
What I did was to force the timeout/completion notifications to enter a critical section in the socket object. Once in, the winner can set a socket state variable and perform its action, whatever that might be. If the I/O completion gets in first, the I/O buffer array is processed in the normal way and any timeout is directed to restart by the state-machine. Similarly if the timeout gets in first, the I/O gets CancelIOEx'd and any later queued completion notification is discarded by the state-engine. Because of these possible 'late' notifications, I put released sockets onto a timeout queue and only recycle them onto the socket object pool after five minutes, in a similar way to how the TCP stack itself puts its sockets into 'TIME_WAIT'.
To do the timeouts, I have one thread that operates on FIFO delta-queues of timing-out objects, one queue for each timeout limit. The thread waits on an input queue for new objects with a timeout calculated from the smallest timeout-expiry-time of the objects at the head of the queues.
There were only a few timeouts used in the server, so I used queues fixed at compile-time. It would be fairly easy to add new queues or modify the timeout by sending appropriate 'command' messages to the thread input queue, mixed-in with the new sockets, but I didn't get that far.
Upon timeout, the thread called an event in the object which, in case of a socket, would enter the socket object CS-protected state-machine, (these was a TimeoutObject class which the socket descended from, amongst other things).
More:
I wait on the semaphore that controls the timeout thread input queue. If it's signaled, I get the new TimeoutObject from the input queue and add it to the end of whatever timeout queue it asks for. If the semaphore wait times out, I check the items at the heads of the timeout FIFO queues and recalculate their remaining interval by sutracting the current time from their timeout time. If the interval is 0 or negative, the timeout event gets called. While iterating the queues and their heads, I keep in a local the minimum remaining interval before the next timeout. Hwn all the head items in all the queues have non-zero remaining interval, I go back to waiting on the queue semaphore using the minimum remaining interval I have accumulated.
The event call returns an enumeration. This enumeration instructs the timeout thread how to handle an object whose event it's just fired. One option is to restart the timeout by recalcuating the timeout-time and pushing the object back onto its timeout queue at the end.
I did not use RegisterWaitForSingleObject() because it needed .NET and my Delphi server was all unmanaged, (I wrote my server a long time ago!).
That, and because, IIRC, it has a limit of 64 handles, like WaitForMultipleObjects(). My server had upwards of 23000 clients timing out. I found the single timeout thread and multiple FIFO queues to be more flexible - any old object could be timed out on it as long as it was descended from TimeoutObject - no extra OS calls/handles needed.
The basic idea is that, since you're using asynchronous I/O with the system thread pool, you shouldn't need to check for timeouts via events because you're not blocking any threads.
The recommended way to check for stale connections is to call getsockopt with the SO_CONNECT_TIME option. This returns the number of seconds that the socket has been connected. I know that's a poll operation, but if you're smart about how and when you query this value, it's actually a pretty good mechanism for managing connections. I explain below how this is done.
Typically I'll call getsockopt in two places: one is during my completion callback (so that I have a timestamp for the last time that an I/O completion occurred on that socket), and one is in my accept thread.
The accept thread monitors my socket backlog via WSAEventSelect and the FD_ACCEPT parameter. This means that the accept thread only executes when Windows determines that there are incoming connections that require accepting. At this time I enumerate my accepted sockets and query SO_CONNECT_TIME again for each socket. I subtract the timestamp of the connection's last I/O completion from this value, and if the difference is above a specified threshold my code deems the connection as having timed out.

inter-process condition variables in Windows

I know that I can use condition variable to synchronize work between the threads, but is there any class like this (condition variable) to synchronize work between the processes, thanks in advance
Use a pair of named Semaphore objects, one to signal and one as a lock. Named sync objects on Windows are automatically inter-process, which takes care of that part of the job for you.
A class like this would do the trick.
class InterprocessCondVar {
private:
HANDLE mSem; // Used to signal waiters
HANDLE mLock; // Semaphore used as inter-process lock
int mWaiters; // # current waiters
protected:
public:
InterprocessCondVar(std::string name)
: mWaiters(0), mLock(NULL), mSem(NULL)
{
// NOTE: You'll need a real "security attributes" pointer
// for child processes to see the semaphore!
// "CreateSemaphore" will do nothing but give you the handle if
// the semaphore already exists.
mSem = CreateSemaphore( NULL, 0, std::numeric_limits<LONG>::max(), name.c_str());
std::string lockName = name + "_Lock";
mLock = CreateSemaphore( NULL, 0, 1, lockName.c_str());
if(!mSem || !mLock) {
throw std::runtime_exception("Semaphore create failed");
}
}
virtual ~InterprocessCondVar() {
CloseHandle( mSem);
CloseHandle( mLock);
}
bool Signal();
bool Broadcast();
bool Wait(unsigned int waitTimeMs = INFINITE);
}
A genuine condition variable offers 3 calls:
1) "Signal()": Wake up ONE waiting thread
bool InterprocessCondVar::Signal() {
WaitForSingleObject( mLock, INFINITE); // Lock
mWaiters--; // Lower wait count
bool result = ReleaseSemaphore( mSem, 1, NULL); // Signal 1 waiter
ReleaseSemaphore( mLock, 1, NULL); // Unlock
return result;
}
2) "Broadcast()": Wake up ALL threads
bool InterprocessCondVar::Broadcast() {
WaitForSingleObject( mLock, INFINITE); // Lock
bool result = ReleaseSemaphore( mSem, nWaiters, NULL); // Signal all
mWaiters = 0; // All waiters clear;
ReleaseSemaphore( mLock, 1, NULL); // Unlock
return result;
}
3) "Wait()": Wait for the signal
bool InterprocessCondVar::Wait(unsigned int waitTimeMs) {
WaitForSingleObject( mLock, INFINITE); // Lock
mWaiters++; // Add to wait count
ReleaseSemaphore( mLock, 1, NULL); // Unlock
// This must be outside the lock
return (WaitForSingleObject( mSem, waitTimeMs) == WAIT_OBJECT_0);
}
This should ensure that Broadcast() ONLY wakes up threads & processes that are already waiting, not all future ones too. This is also a VERY heavyweight object. For CondVars that don't need to exist across processes I would create a different class w/ the same API, and use unnamed objects.
You could use named semaphore or named mutex. You could also share memory between processes by shared memory.
For a project I'm working on I needed a condition variable and mutex implementation which can handle dead processes and won't cause other processes to end up in a deadlock in such a case. I implemented the mutex with the native named mutexes provided by the WIN32 api because they can indicate whether a dead process owns the lock by returning WAIT_ABANDONED. The next issue was that I also needed a condition variable I could use across processes together with these mutexes. I started of with the suggestion from user3726672 but soon discovered that there are several issues in which the state of the counter variable and the state of the semaphore ends up being invalid.
After doing some research, I found a paper by Microsoft Research which explains exactly this scenario: Implementing Condition Variables with Semaphores . It uses a separate semaphore for every single thread to solve the mentioned issues.
My final implementation uses a portion of shared memory in which I store a ringbuffer of thread-ids (the id's of the waiting threads). The processes then create their own handle for every named semaphore/thread-id which they have not encountered yet and cache it. The signal/broadcast/wait functions are then quite straight forward and follow the idea of the proposed solution in the paper. Just remember to remove your thread-id from the ringbuffer if your wait operation fails or results in a timeout.
For the Win32 implementation I recommend reading the following documents:
Semaphore Objects and Using Mutex Objects as those describe the functions you'll need for the implementation.
Alternatives: boost::interprocess has some robust mutex emulation support but it is based on spin locks and caused a very high cpu load on our embedded system which was the final reason why we were looking into our own implementation.
#user3726672: Could you update your post to point to this post or to the referenced paper?
Best Regards,
Michael
Update:
I also had a look at an implementation for linux/posix. Turns out pthread already provides everything you'll need. Just put pthread_cond_t and pthread_mutex_t in some shared memory to share it with the other process and initialize both with PTHREAD_PROCESS_SHARED. Also set PTHREAD_MUTEX_ROBUST on the mutex.
Yes. You can use a (named) Mutex for that. Use CreateMutex to create one. You then wait for it (with functions like WaitForSingleObject), and release it when you're done with ReleaseMutex.
For reference, Boost.Interprocess (documentation for version 1.59) has condition variables and much more. Please note, however, that as of this writing, that "Win32 synchronization is too basic".

Resources