Skip to content

Commit f6016b4

Browse files
committed
initial commit
0 parents  commit f6016b4

2 files changed

Lines changed: 223 additions & 0 deletions

File tree

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Lua Notify C API
2+
<!-- ---------------------------------------------------------------------------------------- -->
3+
4+
The *Notify C API* can be used to get asynchronous notifications from native code running
5+
in other threads, e.g. a GUI thread may get a notification interrupting the event loop for
6+
processing result data from another thread, see [lpugl/example10.lua].
7+
8+
* Lua objects implementing the *Notify C API* can receive asynchronous notifications.
9+
* Native C code invoking the *Notify C API* can send asynchronous notifications from any thread.
10+
11+
Lua objects implementing the *Notify C API* must
12+
have an associated meta table entry *_capi_notify* delivered by
13+
the C API function *notify_get_capi()* and the associated
14+
C API function *toNotifier()* must return a valid pointer for the given
15+
notifier object. For further documentation see [notify_capi.h](./notify_capi.h).
16+
17+
<!-- ---------------------------------------------------------------------------------------- -->
18+
19+
### Implementations:
20+
21+
* [mtmsg] buffer objects are implementing the *Notify C API*. If notified, the buffer
22+
receives an empty message, see [mtmsg/example06.lua].
23+
24+
* [mtstates] state objects are implementing the *Notify C API*. If notified, the state's
25+
callback function is invoked without arguments, see [mtstates/example06.lua].
26+
27+
* [nocurses] implements the *Notify C API* to be interruptible while waiting
28+
for keyboard input, see [nocurses/example03.lua].
29+
30+
* [lpugl] world objects are implementing the *Notify C API* to be interruptible while
31+
waiting for GUI events, see [lpugl/example10.lua].
32+
33+
34+
### Invocations:
35+
36+
* [mtmsg] buffer objects are sending notifications to a registered notifier object
37+
if a new message is added to the buffer, see [mtmsg/example06.lua]
38+
or [lpugl/example10.lua].
39+
40+
<!-- ---------------------------------------------------------------------------------------- -->
41+
42+
[mtmsg]: https://github.com/osch/lua-mtmsg
43+
[mtmsg/example06.lua]: https://github.com/osch/lua-mtmsg/blob/master/examples/example06.lua
44+
45+
[mtstates]: https://github.com/osch/lua-mtstates
46+
[mtstates/example06.lua]: https://github.com/osch/lua-mtstates/blob/master/examples/example06.lua
47+
48+
[nocurses]: https://github.com/osch/lua-nocurses
49+
[nocurses/example03.lua]: https://github.com/osch/lua-nocurses/blob/master/examples/example03.lua
50+
51+
[lpugl]: https://github.com/osch/lua-lpugl
52+
[lpugl/example10.lua]: https://github.com/osch/lua-lpugl/blob/master/example/example10.lua
53+
54+
55+
<!-- ---------------------------------------------------------------------------------------- -->
56+
57+
## License
58+
59+
This is free and unencumbered software released into the public domain.
60+
61+
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
62+
software, either in source code form or as a compiled binary, for any purpose,
63+
commercial or non-commercial, and by any means.
64+
65+
<!-- ---------------------------------------------------------------------------------------- -->

notify_capi.h

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#ifndef NOTIFY_CAPI_H
2+
#define NOTIFY_CAPI_H
3+
4+
#define NOTIFY_CAPI_ID_STRING "_capi_notify"
5+
#define NOTIFY_CAPI_VERSION_MAJOR -1
6+
#define NOTIFY_CAPI_VERSION_MINOR 1
7+
#define NOTIFY_CAPI_VERSION_PATCH 0
8+
9+
typedef struct notify_notifier notify_notifier;
10+
typedef struct notify_capi notify_capi;
11+
12+
#ifndef NOTIFY_CAPI_IMPLEMENT_SET_CAPI
13+
# define NOTIFY_CAPI_IMPLEMENT_SET_CAPI 0
14+
#endif
15+
16+
#ifndef NOTIFY_CAPI_IMPLEMENT_GET_CAPI
17+
# define NOTIFY_CAPI_IMPLEMENT_GET_CAPI 0
18+
#endif
19+
20+
/**
21+
* Type for pointer to function that may be called if an error occurs.
22+
* ehdata: void pointer that is given in notify method (see below)
23+
* msg: detailed error message
24+
* msglen: length of error message
25+
*/
26+
typedef void (*notifier_error_handler)(void* ehdata, const char* msg, size_t msglen);
27+
28+
/**
29+
* Notify C API.
30+
*/
31+
struct notify_capi
32+
{
33+
int version_major;
34+
int version_minor;
35+
int version_patch;
36+
37+
/**
38+
* May point to another (incompatible) version of this API implementation.
39+
* NULL if no such implementation exists.
40+
*
41+
* The usage of next_capi makes it possible to implement two or more
42+
* incompatible versions of the C API.
43+
*
44+
* An API is compatible to another API if both have the same major
45+
* version number and if the minor version number of the first API is
46+
* greater or equal than the second one's.
47+
*/
48+
void* next_capi;
49+
50+
/**
51+
* Must return a valid pointer if the Lua object at the given stack
52+
* index is a valid notifier, otherwise must return NULL.
53+
*
54+
* The returned notifier object must be valid as long as the Lua
55+
* object at the given stack index remains valid.
56+
* To keep the notifier object beyond this call, the function
57+
* retainNotifier() should be called (see below).
58+
*/
59+
notify_notifier* (*toNotifier)(lua_State* L, int index);
60+
61+
/**
62+
* Increase the reference counter of the notifier object.
63+
* Must be thread safe.
64+
*
65+
* This function must be called after the function toNotifier()
66+
* as long as the Lua object on the given stack index is
67+
* valid (see above).
68+
*/
69+
void (*retainNotifier)(notify_notifier* n);
70+
71+
/**
72+
* Decrease the reference counter of the notifier object and
73+
* destructs the notifier object if no reference is left.
74+
* Must be thread safe.
75+
*/
76+
void (*releaseNotifier)(notify_notifier* n);
77+
78+
/**
79+
* Calls the notify method of the given notifier object.
80+
* Must be thread safe.
81+
*
82+
* eh: error handling function, may be NULL
83+
* ehdata: additional data that is given to error handling function.
84+
85+
* Returns 0 - on success otherwise an error code != 0.
86+
* 1 - if notifier is closed. The caller is expected to release the notifier.
87+
* Subsequent calls will always result this return code again.
88+
* All other error codes are implementation specific.
89+
*/
90+
int (*notify)(notify_notifier* n, notifier_error_handler eh, void* ehdata);
91+
};
92+
93+
#if NOTIFY_CAPI_IMPLEMENT_SET_CAPI
94+
/**
95+
* Sets the Notify C API into the metatable at the given index.
96+
*
97+
* index: index of the table that is be used as metatable for objects
98+
* that are associated to the given capi.
99+
*/
100+
static int notify_set_capi(lua_State* L, int index, const notify_capi* capi)
101+
{
102+
lua_pushlstring(L, NOTIFY_CAPI_ID_STRING, strlen(NOTIFY_CAPI_ID_STRING)); /* -> key */
103+
void** udata = lua_newuserdata(L, sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1); /* -> key, value */
104+
*udata = (void*)capi;
105+
strcpy((char*)(udata + 1), NOTIFY_CAPI_ID_STRING); /* -> key, value */
106+
lua_rawset(L, (index < 0) ? (index - 2) : index); /* -> */
107+
return 0;
108+
}
109+
#endif /* NOTIFY_CAPI_IMPLEMENT_SET_CAPI */
110+
111+
#if NOTIFY_CAPI_IMPLEMENT_GET_CAPI
112+
/**
113+
* Gives the associated Notify C API for the object at the given stack index.
114+
* Returns NULL, if the object at the given stack index does not have an
115+
* associated Notify C API or only has a Notify C API with incompatible version
116+
* number. If errorReason is not NULL it receives the error reason in this case:
117+
* 1 for incompatible version nummber and 2 for no associated C API at all.
118+
*/
119+
static const notify_capi* notify_get_capi(lua_State* L, int index, int* errorReason)
120+
{
121+
if (luaL_getmetafield(L, index, NOTIFY_CAPI_ID_STRING) != LUA_TNIL) /* -> _capi */
122+
{
123+
void** udata = lua_touserdata(L, -1); /* -> _capi */
124+
125+
if ( udata
126+
&& (lua_rawlen(L, -1) >= sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1)
127+
&& (memcmp((char*)(udata + 1), NOTIFY_CAPI_ID_STRING,
128+
strlen(NOTIFY_CAPI_ID_STRING) + 1) == 0))
129+
{
130+
const notify_capi* capi = *udata; /* -> _capi */
131+
while (capi) {
132+
if ( capi->version_major == NOTIFY_CAPI_VERSION_MAJOR
133+
&& capi->version_minor >= NOTIFY_CAPI_VERSION_MINOR)
134+
{ /* -> _capi */
135+
lua_pop(L, 1); /* -> */
136+
return capi;
137+
}
138+
capi = capi->next_capi;
139+
}
140+
if (errorReason) {
141+
*errorReason = 1;
142+
}
143+
} else { /* -> _capi */
144+
if (errorReason) {
145+
*errorReason = 2;
146+
}
147+
}
148+
lua_pop(L, 1); /* -> */
149+
} else { /* -> */
150+
if (errorReason) {
151+
*errorReason = 2;
152+
}
153+
}
154+
return NULL;
155+
}
156+
#endif /* NOTIFY_CAPI_IMPLEMENT_GET_CAPI */
157+
158+
#endif /* NOTIFY_CAPI_H */

0 commit comments

Comments
 (0)