1 
2 #if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)
3 #  define _CRT_SECURE_NO_WARNINGS
4 #endif
5 
6 #include <rpmalloc.h>
7 #include <thread.h>
8 #include <test.h>
9 
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <math.h>
15 #include <time.h>
16 
17 #define pointer_offset(ptr, ofs) (void*)((char*)(ptr) + (ptrdiff_t)(ofs))
18 #define pointer_diff(first, second) (ptrdiff_t)((const char*)(first) - (const char*)(second))
19 
20 static size_t _hardware_threads;
21 
22 static void
23 test_initialize(void);
24 
25 static int
test_fail(const char * reason)26 test_fail(const char* reason) {
27 	fprintf(stderr, "FAIL: %s\n", reason);
28 	return -1;
29 }
30 
31 static int
test_alloc(void)32 test_alloc(void) {
33 	unsigned int iloop = 0;
34 	unsigned int ipass = 0;
35 	unsigned int icheck = 0;
36 	unsigned int id = 0;
37 	void* addr[8142];
38 	char data[20000];
39 	unsigned int datasize[7] = { 473, 39, 195, 24, 73, 376, 245 };
40 
41 	rpmalloc_initialize();
42 
43 	for (id = 0; id < 20000; ++id)
44 		data[id] = (char)(id % 139 + id % 17);
45 
46 	//Verify that blocks are 16 byte size aligned
47 	void* testptr = rpmalloc(16);
48 	if (rpmalloc_usable_size(testptr) != 16)
49 		return test_fail("Bad base alloc usable size");
50 	rpfree(testptr);
51 	testptr = rpmalloc(32);
52 	if (rpmalloc_usable_size(testptr) != 32)
53 		return test_fail("Bad base alloc usable size");
54 	rpfree(testptr);
55 	testptr = rpmalloc(128);
56 	if (rpmalloc_usable_size(testptr) != 128)
57 		return test_fail("Bad base alloc usable size");
58 	rpfree(testptr);
59 	for (iloop = 0; iloop <= 1024; ++iloop) {
60 		testptr = rpmalloc(iloop);
61 		size_t wanted_usable_size = 16 * ((iloop / 16) + ((!iloop || (iloop % 16)) ? 1 : 0));
62 		if (rpmalloc_usable_size(testptr) != wanted_usable_size)
63 			return test_fail("Bad base alloc usable size");
64 		rpfree(testptr);
65 	}
66 
67 	//Verify medium block sizes (until class merging kicks in)
68 	for (iloop = 1025; iloop <= 6000; ++iloop) {
69 		testptr = rpmalloc(iloop);
70 		size_t wanted_usable_size = 512 * ((iloop / 512) + ((iloop % 512) ? 1 : 0));
71 		if (rpmalloc_usable_size(testptr) != wanted_usable_size)
72 			return test_fail("Bad medium alloc usable size");
73 		rpfree(testptr);
74 	}
75 
76 	//Large reallocation test
77 	testptr = rpmalloc(253000);
78 	testptr = rprealloc(testptr, 151);
79 	if (rpmalloc_usable_size(testptr) != 160)
80 		return test_fail("Bad usable size");
81 	if (rpmalloc_usable_size(pointer_offset(testptr, 16)) != 144)
82 		return test_fail("Bad offset usable size");
83 	rpfree(testptr);
84 
85 	//Reallocation tests
86 	for (iloop = 1; iloop < 24; ++iloop) {
87 		size_t size = 37 * iloop;
88 		testptr = rpmalloc(size);
89 		*((uintptr_t*)testptr) = 0x12345678;
90 		size_t wanted_usable_size = 16 * ((size / 16) + ((size % 16) ? 1 : 0));
91 		if (rpmalloc_usable_size(testptr) != wanted_usable_size)
92 			return test_fail("Bad usable size (alloc)");
93 		testptr = rprealloc(testptr, size + 16);
94 		if (rpmalloc_usable_size(testptr) < (wanted_usable_size + 16))
95 			return test_fail("Bad usable size (realloc)");
96 		if (*((uintptr_t*)testptr) != 0x12345678)
97 			return test_fail("Data not preserved on realloc");
98 		rpfree(testptr);
99 
100 		testptr = rpaligned_alloc(128, size);
101 		*((uintptr_t*)testptr) = 0x12345678;
102 		wanted_usable_size = 16 * ((size / 16) + ((size % 16) ? 1 : 0));
103 		if (rpmalloc_usable_size(testptr) < wanted_usable_size)
104 			return test_fail("Bad usable size (aligned alloc)");
105 		if (rpmalloc_usable_size(testptr) > (wanted_usable_size + 128))
106 			return test_fail("Bad usable size (aligned alloc)");
107 		testptr = rpaligned_realloc(testptr, 128, size + 32, 0, 0);
108 		if (rpmalloc_usable_size(testptr) < (wanted_usable_size + 32))
109 			return test_fail("Bad usable size (aligned realloc)");
110 		if (*((uintptr_t*)testptr) != 0x12345678)
111 			return test_fail("Data not preserved on realloc");
112 		void* unaligned = rprealloc(testptr, size);
113 		if (unaligned != testptr) {
114 			ptrdiff_t diff = pointer_diff(testptr, unaligned);
115 			if (diff < 0)
116 				return test_fail("Bad realloc behaviour");
117 			if (diff >= 128)
118 				return test_fail("Bad realloc behaviour");
119 		}
120 		rpfree(testptr);
121 	}
122 
123 	static size_t alignment[3] = { 0, 64, 256 };
124 	for (iloop = 0; iloop < 64; ++iloop) {
125 		for (ipass = 0; ipass < 8142; ++ipass) {
126 			size_t size = iloop + ipass + datasize[(iloop + ipass) % 7];
127 			char* baseptr = rpaligned_alloc(alignment[ipass % 3], size);
128 			for (size_t ibyte = 0; ibyte < size; ++ibyte)
129 				baseptr[ibyte] = (char)(ibyte & 0xFF);
130 
131 			size_t resize = (iloop * ipass + datasize[(iloop + ipass) % 7]) & 0x2FF;
132 			size_t capsize = (size > resize ? resize : size);
133 			baseptr = rprealloc(baseptr, resize);
134 			for (size_t ibyte = 0; ibyte < capsize; ++ibyte) {
135 				if (baseptr[ibyte] != (char)(ibyte & 0xFF))
136 					return test_fail("Data not preserved on realloc");
137 			}
138 
139 			size_t alignsize = (iloop * ipass + datasize[(iloop + ipass * 3) % 7]) & 0x2FF;
140 			capsize = (capsize > alignsize ? alignsize : capsize);
141 			baseptr = rpaligned_realloc(baseptr, 128, alignsize, resize, 0);
142 			for (size_t ibyte = 0; ibyte < capsize; ++ibyte) {
143 				if (baseptr[ibyte] != (char)(ibyte & 0xFF))
144 					return test_fail("Data not preserved on realloc");
145 			}
146 
147 			rpfree(baseptr);
148 		}
149 	}
150 
151 	for (iloop = 0; iloop < 64; ++iloop) {
152 		for (ipass = 0; ipass < 8142; ++ipass) {
153 			addr[ipass] = rpmalloc(500);
154 			if (addr[ipass] == 0)
155 				return test_fail("Allocation failed");
156 
157 			memcpy(addr[ipass], data + ipass, 500);
158 
159 			for (icheck = 0; icheck < ipass; ++icheck) {
160 				if (addr[icheck] == addr[ipass])
161 					return test_fail("Bad allocation result");
162 				if (addr[icheck] < addr[ipass]) {
163 					if (pointer_offset(addr[icheck], 500) > addr[ipass])
164 						return test_fail("Bad allocation result");
165 				}
166 				else if (addr[icheck] > addr[ipass]) {
167 					if (pointer_offset(addr[ipass], 500) > addr[icheck])
168 						return test_fail("Bad allocation result");
169 				}
170 			}
171 		}
172 
173 		for (ipass = 0; ipass < 8142; ++ipass) {
174 			if (memcmp(addr[ipass], data + ipass, 500))
175 				return test_fail("Data corruption");
176 		}
177 
178 		for (ipass = 0; ipass < 8142; ++ipass)
179 			rpfree(addr[ipass]);
180 	}
181 
182 	for (iloop = 0; iloop < 64; ++iloop) {
183 		for (ipass = 0; ipass < 1024; ++ipass) {
184 			unsigned int cursize = datasize[ipass%7] + ipass;
185 
186 			addr[ipass] = rpmalloc(cursize);
187 			if (addr[ipass] == 0)
188 				return test_fail("Allocation failed");
189 
190 			memcpy(addr[ipass], data + ipass, cursize);
191 
192 			for (icheck = 0; icheck < ipass; ++icheck) {
193 				if (addr[icheck] == addr[ipass])
194 					return test_fail("Identical pointer returned from allocation");
195 				if (addr[icheck] < addr[ipass]) {
196 					if (pointer_offset(addr[icheck], rpmalloc_usable_size(addr[icheck])) > addr[ipass])
197 						return test_fail("Invalid pointer inside another block returned from allocation");
198 				}
199 				else if (addr[icheck] > addr[ipass]) {
200 					if (pointer_offset(addr[ipass], rpmalloc_usable_size(addr[ipass])) > addr[icheck])
201 						return test_fail("Invalid pointer inside another block returned from allocation");
202 				}
203 			}
204 		}
205 
206 		for (ipass = 0; ipass < 1024; ++ipass) {
207 			unsigned int cursize = datasize[ipass%7] + ipass;
208 			if (memcmp(addr[ipass], data + ipass, cursize))
209 				return test_fail("Data corruption");
210 		}
211 
212 		for (ipass = 0; ipass < 1024; ++ipass)
213 			rpfree(addr[ipass]);
214 	}
215 
216 	for (iloop = 0; iloop < 128; ++iloop) {
217 		for (ipass = 0; ipass < 1024; ++ipass) {
218 			addr[ipass] = rpmalloc(500);
219 			if (addr[ipass] == 0)
220 				return test_fail("Allocation failed");
221 
222 			memcpy(addr[ipass], data + ipass, 500);
223 
224 			for (icheck = 0; icheck < ipass; ++icheck) {
225 				if (addr[icheck] == addr[ipass])
226 					return test_fail("Identical pointer returned from allocation");
227 				if (addr[icheck] < addr[ipass]) {
228 					if (pointer_offset(addr[icheck], 500) > addr[ipass])
229 						return test_fail("Invalid pointer inside another block returned from allocation");
230 				}
231 				else if (addr[icheck] > addr[ipass]) {
232 					if (pointer_offset(addr[ipass], 500) > addr[icheck])
233 						return test_fail("Invalid pointer inside another block returned from allocation");
234 				}
235 			}
236 		}
237 
238 		for (ipass = 0; ipass < 1024; ++ipass) {
239 			if (memcmp(addr[ipass], data + ipass, 500))
240 				return test_fail("Data corruption");
241 		}
242 
243 		for (ipass = 0; ipass < 1024; ++ipass)
244 			rpfree(addr[ipass]);
245 	}
246 
247 	rpmalloc_finalize();
248 
249 	for (iloop = 0; iloop < 2048; iloop += 16) {
250 		rpmalloc_initialize();
251 		addr[0] = rpmalloc(iloop);
252 		if (!addr[0])
253 			return test_fail("Allocation failed");
254 		rpfree(addr[0]);
255 		rpmalloc_finalize();
256 	}
257 
258 	for (iloop = 2048; iloop < (64 * 1024); iloop += 512) {
259 		rpmalloc_initialize();
260 		addr[0] = rpmalloc(iloop);
261 		if (!addr[0])
262 			return test_fail("Allocation failed");
263 		rpfree(addr[0]);
264 		rpmalloc_finalize();
265 	}
266 
267 	for (iloop = (64 * 1024); iloop < (2 * 1024 * 1024); iloop += 4096) {
268 		rpmalloc_initialize();
269 		addr[0] = rpmalloc(iloop);
270 		if (!addr[0])
271 			return test_fail("Allocation failed");
272 		rpfree(addr[0]);
273 		rpmalloc_finalize();
274 	}
275 
276 	rpmalloc_initialize();
277 	for (iloop = 0; iloop < (2 * 1024 * 1024); iloop += 16) {
278 		addr[0] = rpmalloc(iloop);
279 		if (!addr[0])
280 			return test_fail("Allocation failed");
281 		rpfree(addr[0]);
282 	}
283 	rpmalloc_finalize();
284 
285 	printf("Memory allocation tests passed\n");
286 
287 	return 0;
288 }
289 
290 static int
test_realloc(void)291 test_realloc(void) {
292 	srand((unsigned int)time(0));
293 
294 	rpmalloc_initialize();
295 
296 	size_t pointer_count = 4096;
297 	void** pointers = rpmalloc(sizeof(void*) * pointer_count);
298 	memset(pointers, 0, sizeof(void*) * pointer_count);
299 
300 	size_t alignments[5] = {0, 16, 32, 64, 128};
301 
302 	for (size_t iloop = 0; iloop < 8000; ++iloop) {
303 		for (size_t iptr = 0; iptr < pointer_count; ++iptr) {
304 			if (iloop)
305 				rpfree(rprealloc(pointers[iptr], rand() % 4096));
306 			pointers[iptr] = rpaligned_alloc(alignments[(iptr + iloop) % 5], iloop + iptr);
307 		}
308 	}
309 
310 	for (size_t iptr = 0; iptr < pointer_count; ++iptr)
311 		rpfree(pointers[iptr]);
312 	rpfree(pointers);
313 
314 	size_t bigsize = 1024 * 1024;
315 	void* bigptr = rpmalloc(bigsize);
316 	while (bigsize < 3 * 1024 * 1024) {
317 		++bigsize;
318 		bigptr = rprealloc(bigptr, bigsize);
319 	}
320 	rpfree(bigptr);
321 
322 	rpmalloc_finalize();
323 
324 	printf("Memory reallocation tests passed\n");
325 
326 	return 0;
327 }
328 
329 static int
test_superalign(void)330 test_superalign(void) {
331 
332 	rpmalloc_initialize();
333 
334 	size_t alignment[] = { 2048, 4096, 8192, 16384, 32768 };
335 	size_t sizes[] = { 187, 1057, 2436, 5234, 9235, 17984, 35783, 72436 };
336 
337 	for (size_t ipass = 0; ipass < 8; ++ipass) {
338 		for (size_t iloop = 0; iloop < 4096; ++iloop) {
339 			for (size_t ialign = 0, asize = sizeof(alignment) / sizeof(alignment[0]); ialign < asize; ++ialign) {
340 				for (size_t isize = 0, ssize = sizeof(sizes) / sizeof(sizes[0]); isize < ssize; ++isize) {
341 					size_t alloc_size = sizes[isize] + iloop + ipass;
342 					uint8_t* ptr = rpaligned_alloc(alignment[ialign], alloc_size);
343 					if (!ptr || ((uintptr_t)ptr & (alignment[ialign] - 1)))
344 						return test_fail("Super alignment allocation failed");
345 					ptr[0] = 1;
346 					ptr[alloc_size - 1] = 1;
347 					rpfree(ptr);
348 				}
349 			}
350 		}
351 	}
352 
353 	rpmalloc_finalize();
354 
355 	printf("Memory super aligned tests passed\n");
356 
357 	return 0;
358 }
359 
360 typedef struct _allocator_thread_arg {
361 	unsigned int        loops;
362 	unsigned int        passes; //max 4096
363 	unsigned int        datasize[32];
364 	unsigned int        num_datasize; //max 32
365 	void**              pointers;
366 	void**              crossthread_pointers;
367 } allocator_thread_arg_t;
368 
369 static void
allocator_thread(void * argp)370 allocator_thread(void* argp) {
371 	allocator_thread_arg_t arg = *(allocator_thread_arg_t*)argp;
372 	unsigned int iloop = 0;
373 	unsigned int ipass = 0;
374 	unsigned int icheck = 0;
375 	unsigned int id = 0;
376 	void** addr;
377 	uint32_t* data;
378 	unsigned int cursize;
379 	unsigned int iwait = 0;
380 	int ret = 0;
381 
382 	rpmalloc_thread_initialize();
383 
384 	addr = rpmalloc(sizeof(void*) * arg.passes);
385 	data = rpmalloc(512 * 1024);
386 	for (id = 0; id < 512 * 1024 / 4; ++id)
387 		data[id] = id;
388 
389 	thread_sleep(1);
390 
391 	for (iloop = 0; iloop < arg.loops; ++iloop) {
392 		for (ipass = 0; ipass < arg.passes; ++ipass) {
393 			cursize = 4 + arg.datasize[(iloop + ipass + iwait) % arg.num_datasize] + ((iloop + ipass) % 1024);
394 
395 			addr[ipass] = rpmalloc(4 + cursize);
396 			if (addr[ipass] == 0) {
397 				ret = test_fail("Allocation failed");
398 				goto end;
399 			}
400 
401 			*(uint32_t*)addr[ipass] = (uint32_t)cursize;
402 			memcpy(pointer_offset(addr[ipass], 4), data, cursize);
403 
404 			for (icheck = 0; icheck < ipass; ++icheck) {
405 				if (addr[icheck] == addr[ipass]) {
406 					ret = test_fail("Identical pointer returned from allocation");
407 					goto end;
408 				}
409 				if (addr[icheck] < addr[ipass]) {
410 					if (pointer_offset(addr[icheck], *(uint32_t*)addr[icheck] + 4) > addr[ipass]) {
411 						ret = test_fail("Invalid pointer inside another block returned from allocation");
412 						goto end;
413 					}
414 				}
415 				else if (addr[icheck] > addr[ipass]) {
416 					if (pointer_offset(addr[ipass], *(uint32_t*)addr[ipass] + 4) > addr[icheck]) {
417 						ret = test_fail("Invalid pointer inside another block returned from allocation");
418 						goto end;
419 					}
420 				}
421 			}
422 		}
423 
424 		for (ipass = 0; ipass < arg.passes; ++ipass) {
425 			cursize = *(uint32_t*)addr[ipass];
426 
427 			if (memcmp(pointer_offset(addr[ipass], 4), data, cursize)) {
428 				ret = test_fail("Data corrupted");
429 				goto end;
430 			}
431 
432 			rpfree(addr[ipass]);
433 		}
434 	}
435 
436 	rpfree(data);
437 	rpfree(addr);
438 
439 	rpmalloc_thread_finalize();
440 
441 end:
442 	thread_exit((uintptr_t)ret);
443 }
444 
445 static void
crossallocator_thread(void * argp)446 crossallocator_thread(void* argp) {
447 	allocator_thread_arg_t arg = *(allocator_thread_arg_t*)argp;
448 	unsigned int iloop = 0;
449 	unsigned int ipass = 0;
450 	unsigned int cursize;
451 	unsigned int iextra = 0;
452 	int ret = 0;
453 
454 	rpmalloc_thread_initialize();
455 
456 	thread_sleep(10);
457 
458 	size_t next_crossthread = 0;
459 	size_t end_crossthread = arg.loops * arg.passes;
460 
461 	void** extra_pointers = rpmalloc(sizeof(void*) * arg.loops * arg.passes);
462 
463 	for (iloop = 0; iloop < arg.loops; ++iloop) {
464 		for (ipass = 0; ipass < arg.passes; ++ipass) {
465 			size_t iarg = (iloop + ipass + iextra++) % arg.num_datasize;
466 			cursize = arg.datasize[iarg] + ((iloop + ipass) % 21);
467 			void* first_addr = rpmalloc(cursize);
468 			if (first_addr == 0) {
469 				ret = test_fail("Allocation failed");
470 				goto end;
471 			}
472 
473 			iarg = (iloop + ipass + iextra++) % arg.num_datasize;
474 			cursize = arg.datasize[iarg] + ((iloop + ipass) % 71);
475 			void* second_addr = rpmalloc(cursize);
476 			if (second_addr == 0) {
477 				ret = test_fail("Allocation failed");
478 				goto end;
479 			}
480 
481 			iarg = (iloop + ipass + iextra++) % arg.num_datasize;
482 			cursize = arg.datasize[iarg] + ((iloop + ipass) % 17);
483 			void* third_addr = rpmalloc(cursize);
484 			if (third_addr == 0) {
485 				ret = test_fail("Allocation failed");
486 				goto end;
487 			}
488 
489 			rpfree(first_addr);
490 			arg.pointers[iloop * arg.passes + ipass] = second_addr;
491 			extra_pointers[iloop * arg.passes + ipass] = third_addr;
492 
493 			while ((next_crossthread < end_crossthread) &&
494 			        arg.crossthread_pointers[next_crossthread]) {
495 				rpfree(arg.crossthread_pointers[next_crossthread]);
496 				arg.crossthread_pointers[next_crossthread] = 0;
497 				++next_crossthread;
498 			}
499 		}
500 	}
501 
502 	for (iloop = 0; iloop < arg.loops; ++iloop) {
503 		for (ipass = 0; ipass < arg.passes; ++ipass) {
504 			rpfree(extra_pointers[(iloop * arg.passes) + ipass]);
505 		}
506 	}
507 
508 	rpfree(extra_pointers);
509 
510 	while (next_crossthread < end_crossthread) {
511 		if (arg.crossthread_pointers[next_crossthread]) {
512 			rpfree(arg.crossthread_pointers[next_crossthread]);
513 			arg.crossthread_pointers[next_crossthread] = 0;
514 			++next_crossthread;
515 		} else {
516 			thread_yield();
517 		}
518 	}
519 
520 end:
521 	rpmalloc_thread_finalize();
522 
523 	thread_exit((uintptr_t)ret);
524 }
525 
526 static void
initfini_thread(void * argp)527 initfini_thread(void* argp) {
528 	allocator_thread_arg_t arg = *(allocator_thread_arg_t*)argp;
529 	unsigned int iloop = 0;
530 	unsigned int ipass = 0;
531 	unsigned int icheck = 0;
532 	unsigned int id = 0;
533 	void* addr[4096];
534 	char data[8192];
535 	unsigned int cursize;
536 	unsigned int iwait = 0;
537 	int ret = 0;
538 
539 	for (id = 0; id < 8192; ++id)
540 		data[id] = (char)id;
541 
542 	thread_yield();
543 
544 	for (iloop = 0; iloop < arg.loops; ++iloop) {
545 		rpmalloc_thread_initialize();
546 
547 		unsigned int max_datasize = 0;
548 		for (ipass = 0; ipass < arg.passes; ++ipass) {
549 			cursize = arg.datasize[(iloop + ipass + iwait) % arg.num_datasize] + ((iloop + ipass) % 1024);
550 			if (cursize > max_datasize)
551 				max_datasize = cursize;
552 
553 			addr[ipass] = rpmalloc(4 + cursize);
554 			if (addr[ipass] == 0) {
555 				ret = test_fail("Allocation failed");
556 				goto end;
557 			}
558 
559 			*(uint32_t*)addr[ipass] = (uint32_t)cursize;
560 			memcpy(pointer_offset(addr[ipass], 4), data, cursize);
561 
562 			for (icheck = 0; icheck < ipass; ++icheck) {
563 				size_t this_size = *(uint32_t*)addr[ipass];
564 				size_t check_size = *(uint32_t*)addr[icheck];
565 				if (this_size != cursize) {
566 					ret = test_fail("Data corrupted in this block (size)");
567 					goto end;
568 				}
569 				if (check_size > max_datasize) {
570 					ret = test_fail("Data corrupted in previous block (size)");
571 					goto end;
572 				}
573 				if (addr[icheck] == addr[ipass]) {
574 					ret = test_fail("Identical pointer returned from allocation");
575 					goto end;
576 				}
577 				if (addr[icheck] < addr[ipass]) {
578 					if (pointer_offset(addr[icheck], check_size + 4) > addr[ipass]) {
579 						ret = test_fail("Invalid pointer inside another block returned from allocation");
580 						goto end;
581 					}
582 				}
583 				else if (addr[icheck] > addr[ipass]) {
584 					if (pointer_offset(addr[ipass], cursize + 4) > addr[icheck]) {
585 						ret = test_fail("Invalid pointer inside another block returned from allocation");
586 						goto end;
587 					}
588 				}
589 			}
590 		}
591 
592 		for (ipass = 0; ipass < arg.passes; ++ipass) {
593 			cursize = *(uint32_t*)addr[ipass];
594 			if (cursize > max_datasize) {
595 				ret = test_fail("Data corrupted (size)");
596 				goto end;
597 			}
598 
599 			if (memcmp(pointer_offset(addr[ipass], 4), data, cursize)) {
600 				ret = test_fail("Data corrupted");
601 				goto end;
602 			}
603 
604 			rpfree(addr[ipass]);
605 		}
606 
607 		rpmalloc_thread_finalize();
608 		thread_yield();
609 	}
610 
611 end:
612 	rpmalloc_thread_finalize();
613 	thread_exit((uintptr_t)ret);
614 }
615 
616 static int
test_threaded(void)617 test_threaded(void) {
618 	uintptr_t thread[32];
619 	uintptr_t threadres[32];
620 	unsigned int i;
621 	size_t num_alloc_threads;
622 	allocator_thread_arg_t arg;
623 
624 	rpmalloc_initialize();
625 
626 	num_alloc_threads = _hardware_threads;
627 	if (num_alloc_threads < 2)
628 		num_alloc_threads = 2;
629 	if (num_alloc_threads > 32)
630 		num_alloc_threads = 32;
631 
632 	arg.datasize[0] = 19;
633 	arg.datasize[1] = 249;
634 	arg.datasize[2] = 797;
635 	arg.datasize[3] = 3058;
636 	arg.datasize[4] = 47892;
637 	arg.datasize[5] = 173902;
638 	arg.datasize[6] = 389;
639 	arg.datasize[7] = 19;
640 	arg.datasize[8] = 2493;
641 	arg.datasize[9] = 7979;
642 	arg.datasize[10] = 3;
643 	arg.datasize[11] = 79374;
644 	arg.datasize[12] = 3432;
645 	arg.datasize[13] = 548;
646 	arg.datasize[14] = 38934;
647 	arg.datasize[15] = 234;
648 	arg.num_datasize = 16;
649 	arg.loops = 100;
650 	arg.passes = 4000;
651 
652 	thread_arg targ;
653 	targ.fn = allocator_thread;
654 	targ.arg = &arg;
655 	for (i = 0; i < num_alloc_threads; ++i)
656 		thread[i] = thread_run(&targ);
657 
658 	thread_sleep(1000);
659 
660 	for (i = 0; i < num_alloc_threads; ++i)
661 		threadres[i] = thread_join(thread[i]);
662 
663 	rpmalloc_finalize();
664 
665 	for (i = 0; i < num_alloc_threads; ++i) {
666 		if (threadres[i])
667 			return -1;
668 	}
669 
670 	printf("Memory threaded tests passed\n");
671 
672 	return 0;
673 }
674 
675 static int
test_crossthread(void)676 test_crossthread(void) {
677 	uintptr_t thread[8];
678 	allocator_thread_arg_t arg[8];
679 	thread_arg targ[8];
680 
681 	rpmalloc_initialize();
682 
683 	size_t num_alloc_threads = _hardware_threads;
684 	if (num_alloc_threads < 2)
685 		num_alloc_threads = 2;
686 	if (num_alloc_threads > 4)
687 		num_alloc_threads = 4;
688 
689 	for (unsigned int ithread = 0; ithread < num_alloc_threads; ++ithread) {
690 		unsigned int iadd = (ithread * (16 + ithread) + ithread) % 128;
691 		arg[ithread].loops = 50;
692 		arg[ithread].passes = 1024;
693 		arg[ithread].pointers = rpmalloc(sizeof(void*) * arg[ithread].loops * arg[ithread].passes);
694 		memset(arg[ithread].pointers, 0, sizeof(void*) * arg[ithread].loops * arg[ithread].passes);
695 		arg[ithread].datasize[0] = 19 + iadd;
696 		arg[ithread].datasize[1] = 249 + iadd;
697 		arg[ithread].datasize[2] = 797 + iadd;
698 		arg[ithread].datasize[3] = 3 + iadd;
699 		arg[ithread].datasize[4] = 7923 + iadd;
700 		arg[ithread].datasize[5] = 344 + iadd;
701 		arg[ithread].datasize[6] = 3892 + iadd;
702 		arg[ithread].datasize[7] = 19 + iadd;
703 		arg[ithread].datasize[8] = 154 + iadd;
704 		arg[ithread].datasize[9] = 39723 + iadd;
705 		arg[ithread].datasize[10] = 15 + iadd;
706 		arg[ithread].datasize[11] = 493 + iadd;
707 		arg[ithread].datasize[12] = 34 + iadd;
708 		arg[ithread].datasize[13] = 894 + iadd;
709 		arg[ithread].datasize[14] = 193 + iadd;
710 		arg[ithread].datasize[15] = 2893 + iadd;
711 		arg[ithread].num_datasize = 16;
712 
713 		targ[ithread].fn = crossallocator_thread;
714 		targ[ithread].arg = &arg[ithread];
715 	}
716 
717 	for (unsigned int ithread = 0; ithread < num_alloc_threads; ++ithread) {
718 		arg[ithread].crossthread_pointers = arg[(ithread + 1) % num_alloc_threads].pointers;
719 	}
720 
721 	for (int iloop = 0; iloop < 32; ++iloop) {
722 		for (unsigned int ithread = 0; ithread < num_alloc_threads; ++ithread)
723 			thread[ithread] = thread_run(&targ[ithread]);
724 
725 		thread_sleep(100);
726 
727 		for (unsigned int ithread = 0; ithread < num_alloc_threads; ++ithread) {
728 			if (thread_join(thread[ithread]) != 0)
729 				return -1;
730 		}
731 	}
732 
733 	for (unsigned int ithread = 0; ithread < num_alloc_threads; ++ithread)
734 		rpfree(arg[ithread].pointers);
735 
736 	rpmalloc_finalize();
737 
738 	printf("Memory cross thread free tests passed\n");
739 
740 	return 0;
741 }
742 
743 static int
test_threadspam(void)744 test_threadspam(void) {
745 	uintptr_t thread[64];
746 	uintptr_t threadres[64];
747 	unsigned int i, j;
748 	size_t num_passes, num_alloc_threads;
749 	allocator_thread_arg_t arg;
750 
751 	rpmalloc_initialize();
752 
753 	num_passes = 100;
754 	num_alloc_threads = _hardware_threads;
755 	if (num_alloc_threads < 2)
756 		num_alloc_threads = 2;
757 	if (num_alloc_threads > 64)
758 		num_alloc_threads = 64;
759 
760 	arg.loops = 500;
761 	arg.passes = 10;
762 	arg.datasize[0] = 19;
763 	arg.datasize[1] = 249;
764 	arg.datasize[2] = 797;
765 	arg.datasize[3] = 3;
766 	arg.datasize[4] = 79;
767 	arg.datasize[5] = 34;
768 	arg.datasize[6] = 389;
769 	arg.num_datasize = 7;
770 
771 	thread_arg targ;
772 	targ.fn = initfini_thread;
773 	targ.arg = &arg;
774 	for (i = 0; i < num_alloc_threads; ++i)
775 		thread[i] = thread_run(&targ);
776 
777 	for (j = 0; j < num_passes; ++j) {
778 		thread_sleep(10);
779 		thread_fence();
780 
781 		for (i = 0; i < num_alloc_threads; ++i) {
782 			threadres[i] = thread_join(thread[i]);
783 			if (threadres[i])
784 				return -1;
785 			thread[i] = thread_run(&targ);
786 		}
787 	}
788 
789 	thread_sleep(1000);
790 
791 	for (i = 0; i < num_alloc_threads; ++i)
792 		threadres[i] = thread_join(thread[i]);
793 
794 	rpmalloc_finalize();
795 
796 	for (i = 0; i < num_alloc_threads; ++i) {
797 		if (threadres[i])
798 			return -1;
799 	}
800 
801 	printf("Memory thread spam tests passed\n");
802 
803 	return 0;
804 }
805 
806 int
test_run(int argc,char ** argv)807 test_run(int argc, char** argv) {
808 	(void)sizeof(argc);
809 	(void)sizeof(argv);
810 	test_initialize();
811 	if (test_alloc())
812 		return -1;
813 	if (test_realloc())
814 		return -1;
815 	if (test_superalign())
816 		return -1;
817 	if (test_crossthread())
818 		return -1;
819 	if (test_threadspam())
820 		return -1;
821 	if (test_threaded())
822 		return -1;
823 	printf("All tests passed\n");
824 	return 0;
825 }
826 
827 #if (defined(__APPLE__) && __APPLE__)
828 #  include <TargetConditionals.h>
829 #  if defined(__IPHONE__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)
830 #    define NO_MAIN 1
831 #  endif
832 #elif (defined(__linux__) || defined(__linux))
833 #  include <sched.h>
834 #endif
835 
836 #if !defined(NO_MAIN)
837 
838 int
main(int argc,char ** argv)839 main(int argc, char** argv) {
840 	return test_run(argc, argv);
841 }
842 
843 #endif
844 
845 #ifdef _WIN32
846 #include <Windows.h>
847 
848 static void
test_initialize(void)849 test_initialize(void) {
850 	SYSTEM_INFO system_info;
851 	GetSystemInfo(&system_info);
852 	_hardware_threads = (size_t)system_info.dwNumberOfProcessors;
853 }
854 
855 #elif (defined(__linux__) || defined(__linux))
856 
857 static void
test_initialize(void)858 test_initialize(void) {
859 	cpu_set_t prevmask, testmask;
860 	CPU_ZERO(&prevmask);
861 	CPU_ZERO(&testmask);
862 	sched_getaffinity(0, sizeof(prevmask), &prevmask);     //Get current mask
863 	sched_setaffinity(0, sizeof(testmask), &testmask);     //Set zero mask
864 	sched_getaffinity(0, sizeof(testmask), &testmask);     //Get mask for all CPUs
865 	sched_setaffinity(0, sizeof(prevmask), &prevmask);     //Reset current mask
866 	int num = CPU_COUNT(&testmask);
867 	_hardware_threads = (size_t)(num > 1 ? num : 1);
868 }
869 
870 #else
871 
872 static void
test_initialize(void)873 test_initialize(void) {
874 	_hardware_threads = 1;
875 }
876 
877 #endif
878