⛏️ index : haiku.git

/*
 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>


#ifdef __HAIKU__
#	include <OS.h>
#else

typedef int64_t bigtime_t;

static bigtime_t
system_time()
{
	timeval tv;
	gettimeofday(&tv, NULL);
	return (bigtime_t)tv.tv_sec * 1000000 + tv.tv_usec;
}


#endif	// !__HAIKU__

#include "TestUnitUtils.h"

#if 0
static void
dump_sem(const char* name, sem_t* sem)
{
	printf("%s, %p: ", name, sem);
	for (size_t i = 0; i < sizeof(sem_t); i++)
		printf("%02x", ((char*)sem)[i]);
	printf("\n");
}
#endif


static const char* const kSemName1 = "/test_sem1";


static void
test_open_close_unlink()
{
	TEST_SET("sem_{open,close,unlink}()");

	const char* currentTest = NULL;

	// open non-existing with O_CREAT
	TEST("sem_open(O_CREAT) non-existing");
	sem_t* sem = sem_open(kSemName1, O_CREAT, S_IRUSR | S_IWUSR, 1);
	assert_posix_bool_success(sem != SEM_FAILED);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	// open existing with O_CREAT
	TEST("sem_open(O_CREAT) existing");
	sem = sem_open(kSemName1, O_CREAT, S_IRUSR | S_IWUSR, 1);
	assert_posix_bool_success(sem != SEM_FAILED);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	// open existing without O_CREAT
	TEST("sem_open() existing");
	sem = sem_open(kSemName1, 0);
	assert_posix_bool_success(sem != SEM_FAILED);

	// re-open existing without O_CREAT
	TEST("sem_open() existing");
	sem_t* sem2 = sem_open(kSemName1, 0);
	assert_posix_bool_success(sem2 != SEM_FAILED);
	assert_equals(sem, sem2);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	// open existing with O_CREAT | O_EXCL
	TEST("sem_open(O_CREAT | O_EXCL) existing");
	sem = sem_open(kSemName1, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1);
	assert_posix_bool_error(EEXIST, sem != SEM_FAILED);

	// open existing without O_CREAT
	TEST("sem_open() existing");
	sem = sem_open(kSemName1, 0);
	assert_posix_bool_success(sem != SEM_FAILED);

	// unlink
	TEST("unlink() existing");
	assert_posix_success(sem_unlink(kSemName1));

	// open non-existing with O_CREAT | O_EXCL
	TEST("sem_open(O_CREAT | O_EXCL) non-existing");
	sem2 = sem_open(kSemName1, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 2);
	assert_posix_bool_success(sem2 != SEM_FAILED);
	assert_equals_not(sem, sem2);

	// unlink
	TEST("unlink() existing");
	assert_posix_success(sem_unlink(kSemName1));

	// unlink
	TEST("unlink() non-existing");
	assert_posix_error(ENOENT, sem_unlink(kSemName1));

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem2));

	TEST("done");
}


static void
test_init_destroy()
{
	TEST_SET("sem_{init,destroy}()");

	const char* currentTest = NULL;

	// init
	TEST("sem_init()");
	sem_t sem;
	assert_posix_success(sem_init(&sem, 0, 1));

	// destroy
	TEST("sem_destroy()");
	assert_posix_success(sem_destroy(&sem));

	// init
	TEST("sem_init()");
	assert_posix_success(sem_init(&sem, 0, 1));

	// init
	TEST("sem_init()");
	sem_t sem2;
	assert_posix_success(sem_init(&sem2, 0, 2));

	// destroy
	TEST("sem_destroy()");
	assert_posix_success(sem_destroy(&sem));

	// destroy
	TEST("sem_destroy()");
	assert_posix_success(sem_destroy(&sem2));

	TEST("done");
}


static void
test_open_close_fork()
{
	TEST_SET("sem_{open,close}() with fork()");

	const char* currentTest = NULL;

	// open non-existing with O_CREAT
	TEST("sem_open(O_CREAT) non-existing");
	sem_t* sem = sem_open(kSemName1, O_CREAT, S_IRUSR | S_IWUSR, 1);
	assert_posix_bool_success(sem != SEM_FAILED);

	TEST("close_sem() forked");
	pid_t child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		assert_posix_success(sem_close(sem));
		exit(0);
	} else {
		// parent
		assert_posix_success(sem_close(sem));
		wait_for_child(child);
	}

	TEST("sem_open() existing forked");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sem = sem_open(kSemName1, O_CREAT);
		assert_posix_bool_success(sem != SEM_FAILED);
		exit(0);
	} else {
		// parent
		sem = sem_open(kSemName1, O_CREAT);
		wait_for_child(child);
		assert_posix_success(sem_close(sem));
	}

	TEST("done");
}


static void
test_init_destroy_fork()
{
	TEST_SET("sem_{init,destroy}() with fork()");

	const char* currentTest = NULL;

	// init
	TEST("sem_init()");
	sem_t sem;
	assert_posix_success(sem_init(&sem, 0, 1));

	// destroy
	TEST("sem_destroy() forked");
	pid_t child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		assert_posix_success(sem_destroy(&sem));
		exit(0);
	} else {
		// parent
		assert_posix_success(sem_destroy(&sem));
		wait_for_child(child);
	}

	TEST("done");
}


static void
test_post_wait_named()
{
	TEST_SET("sem_{post,wait,trywait,timedwait}() named semaphore");

	const char* currentTest = NULL;

	// make sure the sem doesn't exist yet
	sem_unlink(kSemName1);

	// open non-existing with O_CREAT
	TEST("sem_open(O_CREAT) non-existing");
	sem_t* sem = sem_open(kSemName1, O_CREAT, S_IRUSR | S_IWUSR, 1);
	assert_posix_bool_success(sem != SEM_FAILED);

	TEST("sem_getvalue()");
	int value;
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(2, value);

	// wait
	TEST("sem_wait() non-blocking");
	assert_posix_success(sem_wait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// wait
	TEST("sem_wait() non-blocking");
	assert_posix_success(sem_wait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	// re-open existing
	TEST("sem_open() existing");
	sem = sem_open(kSemName1, 0);
	assert_posix_bool_success(sem != SEM_FAILED);

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(2, value);

	// trywait
	TEST("sem_trywait() success");
	assert_posix_success(sem_trywait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// trywait
	TEST("sem_trywait() success");
	assert_posix_success(sem_trywait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// trywait failure
	TEST("sem_trywait() failure");
	assert_posix_error(EAGAIN, sem_trywait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(2, value);

	// timedwait
	TEST("sem_timedwait() success");
	timespec timeout;
	assert_posix_success(sem_timedwait(sem,
		absolute_timeout(timeout, 1000000)));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	TEST("sem_timedwait() success");
	assert_posix_success(sem_timedwait(sem,
		absolute_timeout(timeout, 1000000)));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("sem_timedwait() timeout");
	bigtime_t startTime = system_time();
	assert_posix_error(ETIMEDOUT, sem_timedwait(sem,
		absolute_timeout(timeout, 1000000)));
	bigtime_t diffTime = system_time() - startTime;
	assert_time_equals(1000000, diffTime);

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	TEST("done");
}


static void
test_post_wait_unnamed()
{
	TEST_SET("sem_{post,wait,trywait,timedwait}() unnamed semaphore");

	const char* currentTest = NULL;

	// init
	TEST("sem_init()");
	sem_t _sem;
	assert_posix_success(sem_init(&_sem, 0, 1));
	sem_t* sem = &_sem;

	TEST("sem_getvalue()");
	int value;
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(2, value);

	// wait
	TEST("sem_wait() non-blocking");
	assert_posix_success(sem_wait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// wait
	TEST("sem_wait() non-blocking");
	assert_posix_success(sem_wait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(2, value);

	// trywait
	TEST("sem_trywait() success");
	assert_posix_success(sem_trywait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// trywait
	TEST("sem_trywait() success");
	assert_posix_success(sem_trywait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// trywait failure
	TEST("sem_trywait() failure");
	assert_posix_error(EAGAIN, sem_trywait(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// post
	TEST("sem_post() no waiting");
	assert_posix_success(sem_post(sem));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(2, value);

	// timedwait
	TEST("sem_timedwait() success");
	timespec timeout;
	assert_posix_success(sem_timedwait(sem,
		absolute_timeout(timeout, 1000000)));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	TEST("sem_timedwait() success");
	assert_posix_success(sem_timedwait(sem,
		absolute_timeout(timeout, 1000000)));

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("sem_timedwait() timeout");
	bigtime_t startTime = system_time();
	assert_posix_error(ETIMEDOUT, sem_timedwait(sem,
		absolute_timeout(timeout, 1000000)));
	bigtime_t diffTime = system_time() - startTime;
	assert_time_equals(1000000, diffTime);

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// destroy
	TEST("sem_destroy()");
	assert_posix_success(sem_destroy(sem));

	TEST("done");
}


static void
test_post_wait_named_fork()
{
	TEST_SET("sem_{post,wait,trywait,timedwait}() named semaphore with fork()");

	const char* currentTest = NULL;

	// make sure the sem doesn't exist yet
	sem_unlink(kSemName1);

	// open non-existing with O_CREAT
	TEST("sem_open(O_CREAT) non-existing");
	sem_t* sem = sem_open(kSemName1, O_CREAT, S_IRUSR | S_IWUSR, 0);
	assert_posix_bool_success(sem != SEM_FAILED);

	TEST("sem_getvalue()");
	int value;
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock child after wait");
	pid_t child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		bigtime_t startTime = system_time();
		assert_posix_success(sem_wait(sem));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sleep(1);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock parent after wait");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sleep(1);
		assert_posix_success(sem_post(sem));

		exit(0);
	} else {
		// parent
		bigtime_t startTime = system_time();
		assert_posix_success(sem_wait(sem));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock child after wait before timeout");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		timespec timeout;
		bigtime_t startTime = system_time();
		assert_posix_success(sem_timedwait(sem,
			absolute_timeout(timeout, 2000000)));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sleep(1);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock child after wait after timeout");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		timespec timeout;
		bigtime_t startTime = system_time();
		assert_posix_error(ETIMEDOUT, sem_timedwait(sem,
			absolute_timeout(timeout, 1000000)));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sleep(2);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	TEST("done");
}


static void
test_post_wait_named_fork2()
{
	TEST_SET("sem_{post,wait,trywait,timedwait}() named semaphore open after "
		"fork");

	const char* currentTest = NULL;

	// make sure the sem doesn't exist yet
	sem_unlink(kSemName1);

	// open non-existing with O_CREAT
	TEST("sem_open(O_CREAT) non-existing");
	sem_t* sem = sem_open(kSemName1, O_CREAT, S_IRUSR | S_IWUSR, 0);
	assert_posix_bool_success(sem != SEM_FAILED);

	TEST("sem_getvalue()");
	int value;
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));
	sem = NULL;

	TEST("unblock child after wait");
	pid_t child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sem = sem_open(kSemName1, 0);
		assert_posix_bool_success(sem != SEM_FAILED);

		bigtime_t startTime = system_time();
		assert_posix_success(sem_wait(sem));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sem = sem_open(kSemName1, 0);
		assert_posix_bool_success(sem != SEM_FAILED);

		sleep(1);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));
	sem = NULL;

	TEST("unblock child after wait before timeout");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sem = sem_open(kSemName1, 0);
		assert_posix_bool_success(sem != SEM_FAILED);

		timespec timeout;
		bigtime_t startTime = system_time();
		assert_posix_success(sem_timedwait(sem,
			absolute_timeout(timeout, 2000000)));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sem = sem_open(kSemName1, 0);
		assert_posix_bool_success(sem != SEM_FAILED);

		sleep(1);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));
	sem = NULL;

	TEST("unblock child after wait after timeout");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sem = sem_open(kSemName1, 0);
		assert_posix_bool_success(sem != SEM_FAILED);

		timespec timeout;
		bigtime_t startTime = system_time();
		assert_posix_error(ETIMEDOUT, sem_timedwait(sem,
			absolute_timeout(timeout, 1000000)));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sem = sem_open(kSemName1, 0);
		assert_posix_bool_success(sem != SEM_FAILED);

		sleep(2);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// close
	TEST("sem_close()");
	assert_posix_success(sem_close(sem));

	TEST("done");
}


static void
test_post_wait_unnamed_fork()
{
	TEST_SET("sem_{post,wait,trywait,timedwait}() unnamed semaphore with "
		"fork()");

	const char* currentTest = NULL;

	// init
	TEST("sem_init()");
	sem_t _sem;
	assert_posix_success(sem_init(&_sem, 0, 1));
	sem_t* sem = &_sem;

	TEST("sem_getvalue()");
	int value;
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	TEST("sem_wait() on fork()ed unnamed sem in parent and child");
	pid_t child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sleep(1);
		assert_posix_success(sem_wait(sem));

		assert_posix_success(sem_getvalue(sem, &value));
		assert_equals(0, value);

		exit(0);
	} else {
		// parent
		assert_posix_success(sem_wait(sem));
		assert_posix_success(sem_getvalue(sem, &value));
		assert_equals(0, value);

		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("sem_post() on fork()ed unnamed sem in parent and child");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		assert_posix_success(sem_post(sem));

		assert_posix_success(sem_getvalue(sem, &value));
		assert_equals(1, value);

		exit(0);
	} else {
		// parent
		assert_posix_success(sem_post(sem));
		assert_posix_success(sem_getvalue(sem, &value));
		assert_equals(1, value);

		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// destroy
	TEST("sem_destroy()");
	assert_posix_success(sem_destroy(sem));

	TEST("done");
}


static void
test_post_wait_unnamed_fork_shared()
{
	TEST_SET("sem_{post,wait,trywait,timedwait}() unnamed semaphore with "
		"fork() in shared memory");

	const char* currentTest = NULL;

	// create shared memory area
	void* address = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
		MAP_SHARED | MAP_ANON, -1, 0);
	assert_posix_bool_success(address != MAP_FAILED);

	// init
	TEST("sem_init()");
	sem_t* sem = (sem_t*)address;
	assert_posix_success(sem_init(sem, 1, 0));

	TEST("sem_getvalue()");
	int value;
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock child after wait");
	pid_t child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		bigtime_t startTime = system_time();
		assert_posix_success(sem_wait(sem));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sleep(1);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock parent after wait");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		sleep(1);
		assert_posix_success(sem_post(sem));

		exit(0);
	} else {
		// parent
		bigtime_t startTime = system_time();
		assert_posix_success(sem_wait(sem));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock child after wait before timeout");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		timespec timeout;
		bigtime_t startTime = system_time();
		assert_posix_success(sem_timedwait(sem,
			absolute_timeout(timeout, 2000000)));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sleep(1);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(0, value);

	TEST("unblock child after wait after timeout");
	child = fork();
	assert_posix_bool_success(child >= 0);

	if (child == 0) {
		// child
		timespec timeout;
		bigtime_t startTime = system_time();
		assert_posix_error(ETIMEDOUT, sem_timedwait(sem,
			absolute_timeout(timeout, 1000000)));
		bigtime_t diffTime = system_time() - startTime;
		assert_time_equals(1000000, diffTime);

		exit(0);
	} else {
		// parent
		sleep(2);
		assert_posix_success(sem_post(sem));
		wait_for_child(child);
	}

	TEST("sem_getvalue()");
	assert_posix_success(sem_getvalue(sem, &value));
	assert_equals(1, value);

	// destroy
	TEST("sem_destroy()");
	assert_posix_success(sem_destroy(sem));

	// unmap memory
	assert_posix_success(munmap(address, 4096));

	TEST("done");
}


int
main()
{
	test_open_close_unlink();
	test_init_destroy();
	test_open_close_fork();
	test_init_destroy_fork();
	test_post_wait_named();
	test_post_wait_unnamed();
	test_post_wait_named_fork();
	test_post_wait_named_fork2();
	test_post_wait_unnamed_fork();
	test_post_wait_unnamed_fork_shared();

	printf("\nall tests OK\n");
}