1 /*
2 * Copyright (c) 2015-2021 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #if !defined(_WIN32)
19 # include <sys/mman.h>
20 #endif
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <moony.h>
26 #include <api_vm.h>
27
28 #include <lualib.h>
29 #include <lauxlib.h>
30
31 #include <laes128.h>
32
33 //FIXME put those into a header
34 extern int luaopen_lpeg(lua_State *L);
35 extern int luaopen_base64(lua_State *L);
36 extern int luaopen_ascii85(lua_State *L);
37 extern int luaopen_mathx(lua_State *L);
38 extern int luaopen_complex(lua_State *L);
39 extern int luaopen_random(lua_State *L);
40
41 //#define MOONY_LOG_MEM
42 #ifdef MOONY_LOG_MEM
43 __realtime static inline void
_log_mem(moony_vm_t * vm,void * ptr,size_t osize,size_t nsize)44 _log_mem(moony_vm_t *vm, void *ptr, size_t osize, size_t nsize)
45 {
46 moony_t *moony = vm->data;
47
48 if(moony->log)
49 {
50 char suffix0 = ' ';
51 size_t space = vm->space;
52 if(space >= 1024)
53 {
54 suffix0 = 'K';
55 space >>= 10;
56 }
57 if(space >= 1024)
58 {
59 suffix0 = 'M';
60 space >>= 10;
61 }
62
63 char suffix1 = ' ';
64 size_t used = vm->used;
65 if(used >= 1024)
66 {
67 suffix1 = 'K';
68 used >>= 10;
69 }
70 if(used >= 1024)
71 {
72 suffix1 = 'M';
73 used >>= 10;
74 }
75
76 lv2_log_trace(&moony->logger, "space: %4zu%c, used: %4zu%c, old: %4zu, new: %4zu, data: %p\n",
77 space, suffix0, used, suffix1, osize, nsize, ptr);
78 }
79 }
80 #endif
81
82 __realtime inline void *
moony_rt_alloc(moony_vm_t * vm,size_t nsize)83 moony_rt_alloc(moony_vm_t *vm, size_t nsize)
84 {
85 vm->used += nsize;
86 if(vm->used > (vm->space >> 1))
87 moony_vm_mem_extend(vm);
88
89 #ifdef MOONY_LOG_MEM
90 _log_mem(vm, NULL, 0, nsize);
91 #endif
92
93 return tlsf_malloc(vm->tlsf, nsize);
94 }
95
96 __realtime inline void *
moony_rt_realloc(moony_vm_t * vm,void * buf,size_t osize,size_t nsize)97 moony_rt_realloc(moony_vm_t *vm, void *buf, size_t osize, size_t nsize)
98 {
99 vm->used -= osize;
100 vm->used += nsize;
101 if(vm->used > (vm->space >> 1))
102 moony_vm_mem_extend(vm);
103
104 #ifdef MOONY_LOG_MEM
105 _log_mem(vm, buf, osize, nsize);
106 #endif
107
108 return tlsf_realloc(vm->tlsf, buf, nsize);
109 }
110
111 __realtime inline void
moony_rt_free(moony_vm_t * vm,void * buf,size_t osize)112 moony_rt_free(moony_vm_t *vm, void *buf, size_t osize)
113 {
114 vm->used -= osize;
115 if(vm->used > (vm->space >> 1))
116 moony_vm_mem_extend(vm);
117
118 #ifdef MOONY_LOG_MEM
119 _log_mem(vm, buf, osize, 0);
120 #endif
121
122 tlsf_free(vm->tlsf, buf);
123 }
124
125 __realtime static void *
lua_alloc(void * ud,void * ptr,size_t osize,size_t nsize)126 lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
127 {
128 moony_vm_t *vm = ud;
129
130 if(nsize == 0)
131 {
132 if(ptr)
133 moony_rt_free(vm, ptr, osize);
134 return NULL;
135 }
136 else
137 {
138 if(ptr)
139 return moony_rt_realloc(vm, ptr, osize, nsize);
140 else
141 return moony_rt_alloc(vm, nsize);
142 }
143 }
144
145 __non_realtime moony_vm_t *
moony_vm_new(size_t mem_size,bool testing,void * data)146 moony_vm_new(size_t mem_size, bool testing, void *data)
147 {
148 moony_vm_t *vm = calloc(1, sizeof(moony_vm_t));
149 if(!vm)
150 return NULL;
151
152 vm->data = data;
153
154 // initialize array of increasing pool sizes
155 vm->size[0] = mem_size;
156
157 // allocate first pool
158 vm->area[0] = moony_vm_mem_alloc(vm->size[0]);
159 if(!vm->area[0])
160 {
161 free(vm);
162 return NULL;
163 }
164
165 vm->tlsf = tlsf_create_with_pool(vm->area[0], vm->size[0]);
166 if(!vm->tlsf)
167 {
168 moony_vm_mem_free(vm->area[0], vm->size[0]);
169 free(vm);
170 return NULL;
171 }
172
173 vm->pool[0] = tlsf_get_pool(vm->tlsf);
174 vm->space += vm->size[0];
175
176 lua_State *L = lua_newstate(lua_alloc, vm);
177 if(!L)
178 {
179 free(vm);
180 return NULL;
181 }
182
183 vm->L = L;
184
185 const int n = lua_gettop(L);
186
187 luaL_requiref(L, "base", luaopen_base, 0);
188
189 luaL_requiref(L, "coroutine", luaopen_coroutine, 1);
190 luaL_requiref(L, "table", luaopen_table, 1);
191 luaL_requiref(L, "string", luaopen_string, 1);
192 luaL_requiref(L, "math", luaopen_math, 1);
193 luaL_requiref(L, "utf8", luaopen_utf8, 1);
194 luaL_requiref(L, "debug", luaopen_debug, 1);
195
196 luaL_requiref(L, "lpeg", luaopen_lpeg, 1);
197 luaL_requiref(L, "base64", luaopen_base64, 1);
198 luaL_requiref(L, "ascii85", luaopen_ascii85, 1);
199 luaL_requiref(L, "aes128", luaopen_aes128, 1);
200 luaL_requiref(L, "mathx", luaopen_mathx, 1);
201 luaL_requiref(L, "complex", luaopen_complex, 1);
202 luaL_requiref(L, "random", luaopen_random, 1);
203
204 if(testing)
205 {
206 luaL_requiref(L, "io", luaopen_io, 1);
207 luaL_requiref(L, "package", luaopen_package, 1);
208 //luaL_requiref(L, "os", luaopen_os, 1);
209 //luaL_requiref(L, "bit32", luaopen_bit32, 1);
210 }
211
212 lua_settop(L, n);
213
214 if(!testing)
215 {
216 // clear dofile
217 lua_pushnil(L);
218 lua_setglobal(L, "dofile");
219
220 // clear loadfile
221 lua_pushnil(L);
222 lua_setglobal(L, "loadfile");
223 }
224
225 // clear math.random[seed]
226 lua_getglobal(L, "math");
227
228 lua_pushnil(L);
229 lua_setfield(L, -2, "random");
230
231 lua_pushnil(L);
232 lua_setfield(L, -2, "randomseed");
233
234 lua_pop(L, 1); // math
235
236 #if USE_MANUAL_GC
237 // manual garbage collector
238 lua_gc(L, LUA_GCSTOP, 0); // disable automatic garbage collection
239 // set step size to run 'as fast as memory allocation'
240 lua_gc(L, LUA_GCINC, 0, 100, 13);
241 #elif USE_INCREMENTAL_GC
242 // incremental garbage collector
243 lua_gc(L, LUA_GCRESTART, 0); // enable automatic garbage collection
244 // next step when memory increased by 5%
245 // run 5% faster than memory allocation
246 lua_gc(L, LUA_GCINC, 105, 105, 13);
247 #elif USE_GENERATIONAL_GC
248 // generational garbage collector
249 lua_gc(L, LUA_GCRESTART, 0); // enable automatic garbage collection
250 // next minor collection when memory increased by 5%
251 // next major collection when memory increased by 100%
252 lua_gc(L, LUA_GCGEN, 5, 100);
253 #else
254 # error "GC method invalid"
255 #endif
256
257 return vm;
258 }
259
260 __non_realtime void
moony_vm_free(moony_vm_t * vm)261 moony_vm_free(moony_vm_t *vm)
262 {
263 if(vm->L)
264 lua_close(vm->L);
265
266 if(vm->ser.buf)
267 moony_rt_free(vm, vm->ser.buf, vm->ser.size);
268
269 vm->used = 0;
270
271 for(int i=(MOONY_POOL_NUM-1); i>=0; i--)
272 {
273 if(!vm->area[i])
274 continue; // this memory slot is unused, skip it
275
276 tlsf_remove_pool(vm->tlsf, vm->pool[i]);
277 moony_vm_mem_free(vm->area[i], vm->size[i]);
278 vm->space -= vm->size[i];
279
280 vm->area[i] = NULL;
281 vm->pool[i] = NULL;
282 vm->size[i] = 0;
283 }
284
285 assert(vm->space == 0);
286 tlsf_destroy(vm->tlsf);
287 vm->tlsf = NULL;
288
289 free(vm);
290 }
291
292 __non_realtime void *
moony_vm_mem_alloc(size_t size)293 moony_vm_mem_alloc(size_t size)
294 {
295 void *area = NULL;
296
297 //printf("moony_vm_mem_alloc: %zu\n", size);
298
299 #if defined(_WIN32)
300 area = _aligned_malloc(size, 8);
301 #else
302 posix_memalign(&area, 8, size);
303 #endif
304 if(!area)
305 return NULL;
306
307 mlock(area, size);
308 memset(area, 0x0, size);
309 return area;
310 }
311
312 __non_realtime void
moony_vm_mem_free(void * area,size_t size)313 moony_vm_mem_free(void *area, size_t size)
314 {
315 if(!area)
316 return;
317
318 //printf("moony_vm_mem_free: %zu\n", size);
319
320 munlock(area, size);
321 free(area);
322 }
323
324 __realtime int
moony_vm_mem_extend(moony_vm_t * vm)325 moony_vm_mem_extend(moony_vm_t *vm)
326 {
327 moony_t *moony = vm->data;
328
329 // request processing or fully extended?
330 if(vm->allocating || vm->fully_extended)
331 return -1;
332
333 for(int i=1; i<MOONY_POOL_NUM; i++)
334 {
335 if(vm->area[i]) // pool already allocated/in-use
336 continue;
337
338 if(vm->nrt)
339 {
340 vm->size[i] = vm->size[i-1] * 2;
341 vm->area[i] = moony_vm_mem_alloc(vm->size[i]);
342 if(vm->area[i])
343 {
344 vm->pool[i] = tlsf_add_pool(vm->tlsf,
345 vm->area[i], vm->size[i]); //FIXME stoat complains about printf
346
347 if(vm->pool[i])
348 {
349 vm->space += vm->size[i];
350 }
351 else
352 {
353 moony_vm_mem_free(vm->area[i], vm->size[i]);
354 vm->size[i] = 0;
355 vm->area[i] = NULL;
356 }
357 }
358 }
359 else
360 {
361 moony_job_t *req;
362 if((req = varchunk_write_request(moony->from_dsp, sizeof(moony_job_t))))
363 {
364 req->type = MOONY_JOB_MEM_ALLOC;
365 req->mem.size = vm->size[i-1] * 2;
366 req->mem.ptr = NULL;
367
368 varchunk_write_advance(moony->from_dsp, sizeof(moony_job_t));
369 if(moony_wake_worker(moony->sched) == LV2_WORKER_SUCCESS)
370 vm->allocating = true; // toggle working flag
371 }
372 }
373
374 return 0;
375 }
376
377 vm->fully_extended = true;
378
379 return -1;
380 }
381
382 void
moony_vm_nrt_enter(moony_vm_t * vm)383 moony_vm_nrt_enter(moony_vm_t *vm)
384 {
385 vm->nrt = true;
386 }
387
388 void
moony_vm_nrt_leave(moony_vm_t * vm)389 moony_vm_nrt_leave(moony_vm_t *vm)
390 {
391 vm->nrt = false;
392 }
393