#include #include #include #include /* heapa.c - kmheapa() "heap alloc" * ~ lexi hale * kmheapa() allocates a pointer on the heap à la libc malloc() * see also: kmheapf() "heap free" */ /* arch specific headers */ #ifdef KFenv_posix # include #endif #include /* we define all our platform functions here, whether or not * they're for the correct platform - only the ones that are * called by the preprocessed form of the code will actually * be linked, linker errors are our friend here! */ kmres kmheapa(sz len) { /* allocate an object on the heap and return * a pointer, or NULL if the allocation failed. */ union { void* raw; ubyte* byte; kmbox* header; } region; /* we need to allocate space for the * header and for the actual object */ sz region_size = sizeof(kmbox) + len; /* kmres reply is the struct that shall be returned by the * function. if an error-condition occurs, the appropriate * error code should be determined through system-specific * means, the pointer value should be set to null, and the * object should be returned immediately. if the object is * not returned from within the system-specific section it * will then be filled out with the appropriate values for * a successful allocation and returned to the caller. */ kmres reply; # ifdef KFenv_posix /* posix APIs - we've got it easy. currently for * nonlinear heap allocation kmheapa simply uses * m(un)map and lets the kernel worry about it. it * may ultimately be worth replacing this with a * more sophisticated implementation, most likely an * existing allocator like jemalloc, though i'm wary * of including outside code - it creates a licensing * mess and i'd prefer libk to be AGPLv3 across the * board. possibility: include hooks for multiple * allocators, allowing the user to select & link in * her preferred allocator at compile time? */ /* because munmap needs to be informed of the size of * the region we are going to unmap, we need to store * that information in the region that we are mapping. * the user will receive an adjusted pointer that can * be adjusted to point a field of type size_t that * contains the size of the allocated space.*/ k_platform_syscall_arg args[] = { null, region_size, posix_prot_read | posix_prot_write, posix_flag_anonymous | posix_map_shared, -1, 0 }; /* impl note: while per manpage fd is "ignored" for * MAP_ANONYMOUS, "some implementations" require a * value of -1, so we're setting it to that just to * be safe. */ struct k_platform_syscall_answer r = k_platform_syscall (k_platform_syscall_mmap, Kmsz(args), args); if (r.error == 0) region.byte = (ubyte*)r.ret; else { switch (r.error) { case k_platform_error_EAGAIN: reply.cond = kmcond_bad_lock; break; case k_platform_error_EINVAL: reply.cond = kmcond_bad_size; break; case k_platform_error_EMFILE: reply.cond = kmcond_too_many; break; case k_platform_error_ENOMEM: reply.cond = kmcond_no_room; break; default: reply.cond = kmcond_fail_assert; } reply.raw = (void*)0; return reply; } # else Knoimpl(kmheapa,KVos); # error missing implementation # endif /* if the system-specific code has not returned early, * we assume success and return the new pointer to the * calling function along with a success code. */ void* const object = (region.byte + sizeof (kmbox)); region.header -> kind = kmkind_heap; region.header -> size = len; reply.cond = kmcond_ok; reply.raw = object; return reply; }