Skip to content

Commit 3b134cc

Browse files
committed
Factor out explicit frontend API
1 parent 6fab3de commit 3b134cc

File tree

10 files changed

+532
-419
lines changed

10 files changed

+532
-419
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.PHONY: all clean run sanitize backends windows full backends-full install
2-
OBJS = core/config.o core/backend.o core/plugin.o
2+
OBJS = core/core.o core/config.o core/backend.o core/plugin.o core/routing.o
33

44
PREFIX ?= /usr
55
PLUGIN_INSTALL = $(PREFIX)/lib/midimonster
@@ -38,7 +38,7 @@ ifdef DEFAULT_CFG
3838
midimonster: CFLAGS += -DDEFAULT_CFG=\"$(DEFAULT_CFG)\"
3939
endif
4040
ifdef PLUGINS
41-
midimonster: CFLAGS += -DPLUGINS=\"$(PLUGINS)\"
41+
core/core.o: CFLAGS += -DPLUGINS=\"$(PLUGINS)\"
4242
PLUGIN_INSTALL = $(PLUGINS)
4343
endif
4444

core/backend.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#include <string.h>
22
#ifndef _WIN32
3-
#define MM_API __attribute__((visibility ("default")))
3+
#define MM_API __attribute__((visibility ("default")))
44
#else
5-
#define MM_API __attribute__((dllexport))
5+
#define MM_API __attribute__((dllexport))
66
#endif
7+
78
#define BACKEND_NAME "core/be"
89
#include "midimonster.h"
910
#include "backend.h"

core/backend.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ int backends_start();
1010
int backends_stop();
1111
instance* mm_instance(backend* b);
1212

13-
/* Backend API */
13+
/* Public backend API */
1414
MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create);
1515
MM_API void mm_channel_update(channel* chan, uint64_t ident);
1616
MM_API instance* mm_instance_find(char* name, uint64_t ident);

core/config.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ typedef struct /*_mm_config_override*/ {
4949
char* value;
5050
} config_override;
5151

52+
/* Internal API */
53+
void config_free();
54+
55+
/* Frontent API */
5256
int config_read(char* file);
5357
int config_add_override(override_type type, char* data);
54-
void config_free();

core/core.c

+257
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#include <string.h>
2+
#include <signal.h>
3+
#include <time.h>
4+
#include <errno.h>
5+
#include <unistd.h>
6+
#ifndef _WIN32
7+
#include <sys/select.h>
8+
#define MM_API __attribute__((visibility ("default")))
9+
#else
10+
#define MM_API __attribute__((dllexport))
11+
#endif
12+
13+
#define BACKEND_NAME "core"
14+
#include "midimonster.h"
15+
#include "core.h"
16+
#include "backend.h"
17+
#include "routing.h"
18+
#include "plugin.h"
19+
#include "config.h"
20+
21+
static size_t fds = 0;
22+
static int max_fd = -1;
23+
static managed_fd* fd = NULL;
24+
static managed_fd* signaled_fds = NULL;
25+
static volatile sig_atomic_t fd_set_dirty = 1;
26+
static uint64_t global_timestamp = 0;
27+
28+
static fd_set all_fds;
29+
30+
MM_API uint64_t mm_timestamp(){
31+
return global_timestamp;
32+
}
33+
34+
static void core_timestamp(){
35+
#ifdef _WIN32
36+
global_timestamp = GetTickCount();
37+
#else
38+
struct timespec current;
39+
if(clock_gettime(CLOCK_MONOTONIC_COARSE, &current)){
40+
fprintf(stderr, "Failed to update global timestamp, time-based processing for some backends may be impaired: %s\n", strerror(errno));
41+
return;
42+
}
43+
44+
global_timestamp = current.tv_sec * 1000 + current.tv_nsec / 1000000;
45+
#endif
46+
}
47+
48+
static fd_set core_collect(int* max_fd){
49+
size_t u = 0;
50+
fd_set rv_fds;
51+
52+
if(max_fd){
53+
*max_fd = -1;
54+
}
55+
56+
DBGPF("Building selector set from %" PRIsize_t " FDs registered to core", fds);
57+
FD_ZERO(&rv_fds);
58+
for(u = 0; u < fds; u++){
59+
if(fd[u].fd >= 0){
60+
FD_SET(fd[u].fd, &rv_fds);
61+
if(max_fd){
62+
*max_fd = max(*max_fd, fd[u].fd);
63+
}
64+
}
65+
}
66+
67+
return rv_fds;
68+
}
69+
70+
MM_API int mm_manage_fd(int new_fd, char* back, int manage, void* impl){
71+
backend* b = backend_match(back);
72+
size_t u;
73+
74+
if(!b){
75+
fprintf(stderr, "Unknown backend %s registered for managed fd\n", back);
76+
return 1;
77+
}
78+
79+
//find exact match
80+
for(u = 0; u < fds; u++){
81+
if(fd[u].fd == new_fd && fd[u].backend == b){
82+
fd[u].impl = impl;
83+
if(!manage){
84+
fd[u].fd = -1;
85+
fd[u].backend = NULL;
86+
fd[u].impl = NULL;
87+
fd_set_dirty = 1;
88+
}
89+
return 0;
90+
}
91+
}
92+
93+
if(!manage){
94+
return 0;
95+
}
96+
97+
//find free slot
98+
for(u = 0; u < fds; u++){
99+
if(fd[u].fd < 0){
100+
break;
101+
}
102+
}
103+
//if necessary expand
104+
if(u == fds){
105+
fd = realloc(fd, (fds + 1) * sizeof(managed_fd));
106+
if(!fd){
107+
fprintf(stderr, "Failed to allocate memory\n");
108+
return 1;
109+
}
110+
111+
signaled_fds = realloc(signaled_fds, (fds + 1) * sizeof(managed_fd));
112+
if(!signaled_fds){
113+
fprintf(stderr, "Failed to allocate memory\n");
114+
return 1;
115+
}
116+
fds++;
117+
}
118+
119+
//store new fd
120+
fd[u].fd = new_fd;
121+
fd[u].backend = b;
122+
fd[u].impl = impl;
123+
fd_set_dirty = 1;
124+
return 0;
125+
}
126+
127+
int core_initialize(){
128+
FD_ZERO(&all_fds);
129+
130+
//load initial timestamp
131+
core_timestamp();
132+
133+
#ifdef _WIN32
134+
WSADATA wsa;
135+
WORD version = MAKEWORD(2, 2);
136+
if(WSAStartup(version, &wsa)){
137+
return 1;
138+
}
139+
_fmode = _O_BINARY;
140+
#endif
141+
142+
//attach plugins
143+
if(plugins_load(PLUGINS)){
144+
LOG("Failed to initialize a backend");
145+
return 1;
146+
}
147+
148+
return 0;
149+
}
150+
151+
int core_start(){
152+
if(backends_start()){
153+
return 1;
154+
}
155+
156+
routing_stats();
157+
158+
if(!fds){
159+
LOG("No descriptors registered for multiplexing");
160+
}
161+
162+
return 0;
163+
}
164+
165+
int core_iteration(){
166+
fd_set read_fds;
167+
struct timeval tv;
168+
int error;
169+
size_t n, u;
170+
#ifdef _WIN32
171+
char* error_message = NULL;
172+
#else
173+
struct timespec ts;
174+
#endif
175+
176+
//rebuild fd set if necessary
177+
if(fd_set_dirty || !signaled_fds){
178+
all_fds = core_collect(&max_fd);
179+
fd_set_dirty = 0;
180+
}
181+
182+
//wait for & translate events
183+
read_fds = all_fds;
184+
tv = backend_timeout();
185+
186+
//check whether there are any fds active, windows does not like select() without descriptors
187+
if(max_fd >= 0){
188+
error = select(max_fd + 1, &read_fds, NULL, NULL, &tv);
189+
if(error < 0){
190+
#ifndef _WIN32
191+
fprintf(stderr, "select failed: %s\n", strerror(errno));
192+
#else
193+
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
194+
NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &error_message, 0, NULL);
195+
fprintf(stderr, "select failed: %s\n", error_message);
196+
LocalFree(error_message);
197+
error_message = NULL;
198+
#endif
199+
return 1;
200+
}
201+
}
202+
else{
203+
DBGPF("No descriptors, sleeping for %zu msec", tv.tv_sec * 1000 + tv.tv_usec / 1000);
204+
#ifdef _WIN32
205+
Sleep(tv.tv_sec * 1000 + tv.tv_usec / 1000);
206+
#else
207+
ts.tv_sec = tv.tv_sec;
208+
ts.tv_nsec = tv.tv_usec * 1000;
209+
nanosleep(&ts, NULL);
210+
#endif
211+
}
212+
213+
//update this iteration's timestamp
214+
core_timestamp();
215+
216+
//find all signaled fds
217+
n = 0;
218+
for(u = 0; u < fds; u++){
219+
if(fd[u].fd >= 0 && FD_ISSET(fd[u].fd, &read_fds)){
220+
signaled_fds[n] = fd[u];
221+
n++;
222+
}
223+
}
224+
225+
//run backend processing to collect events
226+
DBGPF("%" PRIsize_t " backend FDs signaled", nfds);
227+
if(backends_handle(n, signaled_fds)){
228+
return 1;
229+
}
230+
231+
//route generated events
232+
return routing_iteration();
233+
}
234+
235+
static void fds_free(){
236+
size_t u;
237+
for(u = 0; u < fds; u++){
238+
if(fd[u].fd >= 0){
239+
close(fd[u].fd);
240+
fd[u].fd = -1;
241+
}
242+
}
243+
free(fd);
244+
fds = 0;
245+
fd = NULL;
246+
}
247+
248+
void core_shutdown(){
249+
backends_stop();
250+
routing_cleanup();
251+
free(signaled_fds);
252+
signaled_fds = NULL;
253+
fds_free();
254+
plugins_close();
255+
config_free();
256+
fd_set_dirty = 1;
257+
}

core/core.h

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* MIDIMonster frontend API
3+
*
4+
* These APIs expose the core as a linkable module. Frontends will use these calls
5+
* as primary interface to interact with the MIDIMonster core.
6+
*
7+
* The lifecycle is as follows:
8+
*
9+
* * Initially, only the following API calls are valid:
10+
* config_add_override()
11+
* core_initialize()
12+
* This allows the frontend to configure overrides for any configuration
13+
* loaded later (e.g. by parsing command line arguments) before initializing
14+
* the core.
15+
* * Calling core_initialize() attaches all backend modules to the system and
16+
* performs platform specific startup operations. From this point on,
17+
* core_shutdown() must be called before terminating the frontend.
18+
* All frontend API calls except `core_iteration` are now valid.
19+
* The core is now in the configuration stage in which the frontend
20+
* will push any configuration files.
21+
* * Calling core_start() marks the transition from the configuration phase
22+
* to the translation phase. The core will activate any configured backends
23+
* and provide them with the information required to connect to their data
24+
* sources and sinks. In this stage, only the following API calls are valid:
25+
* core_iteration()
26+
* core_shutdown()
27+
* * The frontend will now repeatedly call core_iteration() to process any incoming
28+
* events. This API will block execution until either one or more events have
29+
* been registered or an internal timeout expires.
30+
* * Calling core_shutdown() releases all memory allocated by the core and any
31+
* attached modules or plugins, including all configuration, overrides,
32+
* mappings, statistics, etc. The core is now ready to exit or be
33+
* reinitialized using core_initialize().
34+
*/
35+
36+
int core_initialize();
37+
int core_start();
38+
int core_iteration();
39+
void core_shutdown();
40+
41+
/* Public backend API */
42+
MM_API uint64_t mm_timestamp();
43+
MM_API int mm_manage_fd(int new_fd, char* back, int manage, void* impl);

core/plugin.h

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
typedef int (*plugin_init)();
2+
3+
/* Internal API */
24
int plugins_load(char* dir);
35
int plugins_close();

0 commit comments

Comments
 (0)