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