mmozeiko
Actually its other way around - condition variables are much more lightweight than events. Its better to design your synchronization directly to use semaphores or condition variables instead of events (or use conditional varibles to simulate events).
As I said - if you want Windows event like synchronization, then you can simply use eventfd. No need to simulate them. Here's example how to use it (WARNING - no error checking): https://gist.github.com/mmozeiko/b2a72ab9fe52e6c2b88fff5ec8eaeba7
eventfd actually is more powerful than Windows event, it can handle more complex synchronization situations.
Btw, I hate when platform abstraction libraries create their own types for events / files / sockets / etc. Because that prevents you to use a lot of good synchronization situations. All modern OS'es allow to wait simultaneously on event and file/socket and other handles (WaitForMulipleObjects/epoll). On Windows both file and event has HANDLE type. On Posix - int type. Sure, you can drop down to actual OS primitives if library allows to access struct members, but then its kind of pointless to use abstraction library at all. Basically library at this point makes you to write more inefficient code, but using multithreading/events/etc should be about writing more efficient code.
Thanks for that gist, i will look into that.
Regarding your last statement, i hate it too when libraries introduces more stuff than what is actually needed.
For FPL i just want it to mirror every handle / file / socket to match the OS specific thing directly, without any overhead if possible.
If you check out the all the fpl*Handle structs, you will see that Signals are the only thing which totally breaks this. So its time to clean this up. Also the term "Signal" is wrong, it should be "Event" instead.
Oh and i see that fplThreadHandle contains stuff which i added for testing, but forgot to remove it (Mutex, Condition).
So signals are the only thing which really needs a cleanup.
But why is it ineffecient to have access to the underlying OS primitives? I find it very ineffectient to introduce a allocation just to hide away the internal OS specific handles. Thats what most libraries do, like for example SDL. They will never show the internal stuff.
Style-A (Typical style for most libraries):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | // Header
typedef void *fplEventHandle;
// Impl
typedef struct fplInternalEventHandle {
#if defined(FPL_PLATFORM_WIN32)
HANDLE win32EventHandle;
#elif defined(FPL_PLATFORM_LINUX)
int linuxEventHandle;
#else
int dummy;
#endif
} fplInternalEventHandle;
fplEventHandle fplEventCreate() {
fplInternalEventHandle *internalHandle = (fplInternalEventHandle *)malloc(sizeof(fplInternalEventHandle ));
// ...
fplEventHandle result = (void *)internalHandle;
}
void fplEventDestroy(fplEventHandle *eventHandle) {
fplInternalEventHandle *internalHandle = (fplInternalEventHandle *)eventHandle;
#if defined(FPL_PLATFORM_WIN32)
internalHandle->win32EventHandle ...
#else if defined(FPL_PLATFORM_LINUX)
internalHandle->linuxEventHandle = ...
#endif
free(internalHandle);
}
|
Style-B (Which FPL targets):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | // Header
typedef struct fplEventHandle {
#if defined(FPL_PLATFORM_WIN32)
HANDLE win32EventHandle;
#elif defined(FPL_PLATFORM_LINUX)
int linuxEventHandle;
#else
int dummy;
#endif
} fplEventHandle;
// Impl
bool fplEventInit(fplEventHandle *handle) {
#if defined(FPL_PLATFORM_WIN32)
handle->internalHandle.win32EventHandle ...
#else if defined(FPL_PLATFORM_LINUX)
handle->internalHandle.linuxEventHandle = ...
#endif
}
void fplEventDestroy(fplEventHandle *handle) {
// ...
}
|
Really i dont see any reason why Style-B is ineffecient. It is not ineffecient or slower, but it is more unsafe - because you can break the internal structures more easiely.