/*===========================================================================* * test_mutex * *===========================================================================*/ static void test_mutex(void) { unsigned int i; thread_t t[3]; #ifdef MDEBUG mthread_verify(); #endif if (mthread_mutex_init(&mu[0], NULL) != 0) err(2, 1); if (mthread_mutex_init(&mu[1], NULL) != 0) err(2, 2); if (mthread_mutex_init(&mu[2], NULL) != 0) err(2, 3); if (mthread_create(&t[0], NULL, mutex_a, (void *) mu) != 0) err(2, 3); if (mthread_create(&t[1], NULL, mutex_b, (void *) mu) != 0) err(2, 4); if (mthread_create(&t[2], NULL, mutex_c, (void *) mu) != 0) err(2, 5); if (mthread_mutex_lock(&mu[2]) != 0) err(2, 6); mthread_yield_all(); /* Should result in a RUNNABLE mutex_a, and a blocked * on mutex mutex_b and mutex_c. */ VERIFY_MUTEX(1, 0, 0, 2, 7); /* err(2, 7) */ if (mthread_mutex_unlock(&mu[2]) != 0) err(2, 8); mthread_yield(); /* Should schedule mutex_a to release the lock on the * mu[0] mutex. Consequently allowing mutex_b and mutex_c * to acquire locks on the mutexes and exit. */ VERIFY_MUTEX(2, 0, 0, 2, 9); for (i = 0; i < (sizeof(t) / sizeof(thread_t)); i++) if (mthread_join(t[i], NULL) != 0) err(2, 10); if (mthread_mutex_destroy(&mu[0]) != 0) err(2, 11); if (mthread_mutex_destroy(&mu[1]) != 0) err(2, 12); if (mthread_mutex_destroy(&mu[2]) != 0) err(2, 13); #ifdef MDEBUG mthread_verify(); #endif }
/*===========================================================================* * test_attributes * *===========================================================================*/ static void test_attributes(void) { attr_t tattr; thread_t tid; int detachstate = -1, status = 0; unsigned int i, no_ints, stack_untouched = 1; void *stackaddr, *newstackaddr; int *stackp; size_t stacksize, newstacksize; #ifdef MDEBUG mthread_verify(); #endif /* Initialize thread attribute and try to read the default values */ if (mthread_attr_init(&tattr) != 0) err(11, 1); if (mthread_attr_getdetachstate(&tattr, &detachstate) != 0) err(11, 2); if (detachstate != MTHREAD_CREATE_JOINABLE) err(11, 3); if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 4); if (stackaddr != NULL) err(11, 5); if (stacksize != (size_t) 0) err(11, 6); /* Modify the attribute ... */ /* Try bogus detach state value */ if (mthread_attr_setdetachstate(&tattr, 0xc0ffee) == 0) err(11, 7); if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0) err(11, 8); newstacksize = (size_t) MEG; if ((newstackaddr = malloc(newstacksize)) == NULL) err(11, 9); if (mthread_attr_setstack(&tattr, newstackaddr, newstacksize) != 0) err(11, 10); /* ... and read back the new values. */ if (mthread_attr_getdetachstate(&tattr, &detachstate) != 0) err(11, 11); if (detachstate != MTHREAD_CREATE_DETACHED) err(11, 12); if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 13); if (stackaddr != newstackaddr) err(11, 14); if (stacksize != newstacksize) err(11, 15); if (mthread_attr_destroy(&tattr) != 0) err(11, 16); free(newstackaddr); /* Try to allocate too small a stack; it should fail and the attribute * values should remain as is. */ newstacksize = MTHREAD_STACK_MIN - 1; stackaddr = NULL; stacksize = 0; if (mthread_attr_init(&tattr) != 0) err(11, 17); if ((newstackaddr = malloc(newstacksize)) == NULL) err(11, 18); if (mthread_attr_setstack(&tattr, newstackaddr, newstacksize) != EINVAL) err(11, 19); if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 21); if (stackaddr == newstackaddr) err(11, 22); if (stacksize == newstacksize) err(11, 23); if (mthread_attr_destroy(&tattr) != 0) err(11, 24); free(newstackaddr); /* Tell attribute to let the system allocate a stack for the thread and only * dictate how big that stack should be (2 megabyte, not actually allocated * yet). */ if (mthread_attr_init(&tattr) != 0) err(11, 25); if (mthread_attr_setstack(&tattr, NULL /* System allocated */, 2*MEG) != 0) err(11, 26); if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 27); if (stackaddr != NULL) err(11, 28); if (stacksize != 2*MEG) err(11, 29); /* Use set/getstacksize to set and retrieve new stack sizes */ stacksize = 0; if (mthread_attr_getstacksize(&tattr, &stacksize) != 0) err(11, 30); if (stacksize != 2*MEG) err(11, 31); newstacksize = MEG; if (mthread_attr_setstacksize(&tattr, newstacksize) != 0) err(11, 32); if (mthread_attr_getstacksize(&tattr, &stacksize) != 0) err(11, 33); if (stacksize != newstacksize) err(11, 34); if (mthread_attr_destroy(&tattr) != 0) err(11, 35); /* Perform same tests, but also actually use them in a thread */ if (mthread_attr_init(&tattr) != 0) err(11, 36); if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0) err(11, 37); condition_mutex = &mu[0]; if (mthread_mutex_init(condition_mutex, NULL) != 0) err(11, 38); if (mthread_cond_init(&condition, NULL) != 0) err(11, 39); if (mthread_mutex_lock(condition_mutex) != 0) err(11, 40); if (mthread_create(&tid, &tattr, thread_f, NULL) != 0) err(11, 41); /* Wait for thread_f to finish */ if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 42); if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 43); if (th_f != 1) err(11, 44); /* Joining a detached thread should fail */ if (mthread_join(tid, NULL) == 0) err(11, 45); if (mthread_attr_destroy(&tattr) != 0) err(11, 46); /* Try telling the attribute how large the stack should be */ if (mthread_attr_init(&tattr) != 0) err(11, 47); if (mthread_attr_setstack(&tattr, NULL, 2 * MTHREAD_STACK_MIN) != 0) err(11, 48); if (mthread_mutex_lock(condition_mutex) != 0) err(11, 49); if (mthread_create(&tid, &tattr, thread_g, NULL) != 0) err(11, 50); /* Wait for thread_g to finish */ if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 51); if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 52); if (th_g != 1) err(11, 53); if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0) err(11, 54); /* Shouldn't affect the join below, as thread is already * running as joinable. If this attribute should be * modified after thread creation, use mthread_detach(). */ if (mthread_join(tid, NULL) != 0) err(11, 55); if (mthread_attr_destroy(&tattr) != 0) err(11, 56); /* Try telling the attribute how large the stack should be and where it is * located. */ if (mthread_attr_init(&tattr) != 0) err(11, 57); stacksize = 3 * MEG; /* Make sure this test is meaningful. We have to verify that we actually * use a custom stack. So we're going to allocate an array on the stack in * thread_h that should at least be bigger than the default stack size * allocated by the system. */ if (2 * MEG <= MTHREAD_STACK_MIN) err(11, 58); if ((stackaddr = malloc(stacksize)) == NULL) err(11, 59); /* Fill stack with pattern. We assume that the beginning of the stack * should be overwritten with something and that the end should remain * untouched. The thread will zero-fill around two-thirds of the stack with * zeroes, so we can check if that's true. */ stackp = stackaddr; no_ints = stacksize / sizeof(int); for (i = 0; i < no_ints ; i++) stackp[i] = MAGIC; if (mthread_attr_setstack(&tattr, stackaddr, stacksize) != 0) err(11, 60); if (mthread_mutex_lock(condition_mutex) != 0) err(11, 61); if (mthread_create(&tid, &tattr, thread_h, (void *) &stacksize) != 0) err(11, 62); /* Wait for thread h to finish */ if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 63); if (th_h != 1) err(11, 64); if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 65); /* Verify stack hypothesis; we assume a stack is used from the top and grows * downwards. */ #if (_MINIX_CHIP == _CHIP_INTEL) if (stackp[0] != MAGIC) err(11, 66); /* End of the stack */ for (i = no_ints - 1 - 16; i < no_ints; i++) if (stackp[i] != MAGIC) stack_untouched = 0; if (stack_untouched) err(11, 67); /* Beginning of the stack */ if (stackp[no_ints / 2] != 0) err(11, 68);/*Zero half way through the stack*/ #else #error "Unsupported chip for this test" #endif if (mthread_join(tid, (void *) &status) != 0) err(11, 69); if ((size_t) status != stacksize) err(11, 70); if (mthread_attr_destroy(&tattr) != 0) err(11, 71); if (mthread_mutex_destroy(condition_mutex) != 0) err(11, 72); if (mthread_cond_destroy(&condition) != 0) err(11, 73); free(stackaddr); #ifdef MDEBUG mthread_verify(); #endif }
/*===========================================================================* * test_condition * *===========================================================================*/ static void test_condition(void) { #define NTHREADS 10 int i; thread_t t[2], s[NTHREADS]; count_mutex = &mu[0]; condition_mutex = &mu[1]; /* Test simple condition variable behavior: Two threads increase a counter. * At some point one thread waits for a condition and the other thread * signals the condition. Consequently, one thread increased the counter a * few times less than other thread. Although the difference is 'random', * there is a guaranteed minimum difference that we can measure. */ #ifdef MDEBUG mthread_verify(); #endif if (mthread_mutex_init(count_mutex, NULL) != 0) err(8, 1); if (mthread_mutex_init(condition_mutex, NULL) != 0) err(8, 2); if (mthread_cond_init(&condition, NULL) != 0) err(8, 3); count = 0; if (mthread_create(&t[0], NULL, cond_a, NULL) != 0) err(8, 4); if (mthread_create(&t[1], NULL, cond_b, NULL) != 0) err(8, 5); for (i = 0; i < (sizeof(t) / sizeof(thread_t)); i++) if (mthread_join(t[i], NULL) != 0) err(8, 6); if (mthread_mutex_destroy(count_mutex) != 0) err(8, 7); if (mthread_mutex_destroy(condition_mutex) != 0) err(8, 8); if (mthread_cond_destroy(&condition) != 0) err(8, 9); #ifdef MTHREAD_STRICT /* Let's try to destroy it again. Should fails as it's uninitialized. */ /* Note: this only works when libmthread is compiled with MTHREAD_STRICT. In * POSIX this situation is a MAY fail if... */ if (mthread_cond_destroy(&condition) == 0) err(8, 10); #endif #ifdef MDEBUG mthread_verify(); #endif /* Test signal broadcasting: spawn N threads that will increase a counter * after a condition has been signaled. The counter must equal N. */ if (mthread_mutex_init(count_mutex, NULL) != 0) err(8, 11); if (mthread_mutex_init(condition_mutex, NULL) != 0) err(8, 12); if (mthread_cond_init(&condition, NULL) != 0) err(8, 13); condition_met = count = 0; for (i = 0; i < NTHREADS; i++) if (mthread_create(&s[i], NULL, cond_broadcast, NULL) != 0) err(8, 14); /* Allow other threads to block on the condition variable. If we don't yield, * the threads will only start running when we call mthread_join below. In * that case the while loop in cond_broadcast will never evaluate to true. */ mthread_yield(); if (mthread_mutex_lock(condition_mutex) != 0) err(8, 15); condition_met = 1; if (mthread_cond_broadcast(&condition) != 0) err(8, 16); if (mthread_mutex_unlock(condition_mutex) != 0) err(8, 17); for (i = 0; i < (sizeof(s) / sizeof(thread_t)); i++) if (mthread_join(s[i], NULL) != 0) err(8, 18); if (count != NTHREADS) err(8, 19); if (mthread_mutex_destroy(count_mutex) != 0) err(8, 20); if (mthread_mutex_destroy(condition_mutex) != 0) err(8, 21); if (mthread_cond_destroy(&condition) != 0) err(8, 22); #ifdef MTHREAD_STRICT /* Again, destroying the condition variable twice shouldn't work */ /* See previous note about MTHREAD_STRICT */ if (mthread_cond_destroy(&condition) == 0) err(8, 23); #endif #ifdef MDEBUG mthread_verify(); #endif }
/*===========================================================================* * sef_cb_init_fresh * *===========================================================================*/ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *info) { /* Initialize the virtual file server. */ int s, i; struct fproc *rfp; message mess; struct rprocpub rprocpub[NR_BOOT_PROCS]; force_sync = 0; receive_from = ANY; self = NULL; verbose = 0; /* Initialize proc endpoints to NONE */ for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { rfp->fp_endpoint = NONE; rfp->fp_pid = PID_FREE; } /* Initialize the process table with help of the process manager messages. * Expect one message for each system process with its slot number and pid. * When no more processes follow, the magic process number NONE is sent. * Then, stop and synchronize with the PM. */ do { if ((s = sef_receive(PM_PROC_NR, &mess)) != OK) panic("VFS: couldn't receive from PM: %d", s); if (mess.m_type != PM_INIT) panic("unexpected message from PM: %d", mess.m_type); if (NONE == mess.PM_PROC) break; rfp = &fproc[mess.PM_SLOT]; rfp->fp_flags = FP_NOFLAGS; rfp->fp_pid = mess.PM_PID; rfp->fp_endpoint = mess.PM_PROC; rfp->fp_grant = GRANT_INVALID; rfp->fp_blocked_on = FP_BLOCKED_ON_NONE; rfp->fp_realuid = (uid_t) SYS_UID; rfp->fp_effuid = (uid_t) SYS_UID; rfp->fp_realgid = (gid_t) SYS_GID; rfp->fp_effgid = (gid_t) SYS_GID; rfp->fp_umask = ~0; } while (TRUE); /* continue until process NONE */ mess.m_type = OK; /* tell PM that we succeeded */ s = send(PM_PROC_NR, &mess); /* send synchronization message */ /* All process table entries have been set. Continue with initialization. */ fp = &fproc[_ENDPOINT_P(VFS_PROC_NR)];/* During init all communication with * FSes is on behalf of myself */ init_dmap(); /* Initialize device table. */ system_hz = sys_hz(); /* Map all the services in the boot image. */ if ((s = sys_safecopyfrom(RS_PROC_NR, info->rproctab_gid, 0, (vir_bytes) rprocpub, sizeof(rprocpub), S)) != OK){ panic("sys_safecopyfrom failed: %d", s); } for (i = 0; i < NR_BOOT_PROCS; i++) { if (rprocpub[i].in_use) { if ((s = map_service(&rprocpub[i])) != OK) { panic("VFS: unable to map service: %d", s); } } } /* Subscribe to block and character driver events. */ s = ds_subscribe("drv\\.[bc]..\\..*", DSF_INITIAL | DSF_OVERWRITE); if (s != OK) panic("VFS: can't subscribe to driver events (%d)", s); /* Initialize worker threads */ for (i = 0; i < NR_WTHREADS; i++) { worker_init(&workers[i]); } worker_init(&sys_worker); /* exclusive system worker thread */ worker_init(&dl_worker); /* exclusive worker thread to resolve deadlocks */ /* Initialize global locks */ if (mthread_mutex_init(&pm_lock, NULL) != 0) panic("VFS: couldn't initialize pm lock mutex"); if (mthread_mutex_init(&exec_lock, NULL) != 0) panic("VFS: couldn't initialize exec lock"); if (mthread_mutex_init(&bsf_lock, NULL) != 0) panic("VFS: couldn't initialize block special file lock"); /* Initialize event resources for boot procs and locks for all procs */ for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { if (mutex_init(&rfp->fp_lock, NULL) != 0) panic("unable to initialize fproc lock"); #if LOCK_DEBUG rfp->fp_vp_rdlocks = 0; rfp->fp_vmnt_rdlocks = 0; #endif } init_vnodes(); /* init vnodes */ init_vmnts(); /* init vmnt structures */ init_select(); /* init select() structures */ init_filps(); /* Init filp structures */ mount_pfs(); /* mount Pipe File Server */ worker_start(do_init_root); /* mount initial ramdisk as file system root */ yield(); /* force do_init_root to start */ self = NULL; return(OK); }
/*===========================================================================* * sef_cb_init_fresh * *===========================================================================*/ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *info) { /* Initialize the virtual file server. */ int s, i; struct fproc *rfp; message mess; struct rprocpub rprocpub[NR_BOOT_PROCS]; self = NULL; verbose = 0; /* Initialize proc endpoints to NONE */ for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { rfp->fp_endpoint = NONE; rfp->fp_pid = PID_FREE; } /* Initialize the process table with help of the process manager messages. * Expect one message for each system process with its slot number and pid. * When no more processes follow, the magic process number NONE is sent. * Then, stop and synchronize with the PM. */ do { if ((s = sef_receive(PM_PROC_NR, &mess)) != OK) panic("VFS: couldn't receive from PM: %d", s); if (mess.m_type != VFS_PM_INIT) panic("unexpected message from PM: %d", mess.m_type); if (NONE == mess.VFS_PM_ENDPT) break; rfp = &fproc[mess.VFS_PM_SLOT]; rfp->fp_flags = FP_NOFLAGS; rfp->fp_pid = mess.VFS_PM_PID; rfp->fp_endpoint = mess.VFS_PM_ENDPT; rfp->fp_grant = GRANT_INVALID; rfp->fp_blocked_on = FP_BLOCKED_ON_NONE; rfp->fp_realuid = (uid_t) SYS_UID; rfp->fp_effuid = (uid_t) SYS_UID; rfp->fp_realgid = (gid_t) SYS_GID; rfp->fp_effgid = (gid_t) SYS_GID; rfp->fp_umask = ~0; } while (TRUE); /* continue until process NONE */ mess.m_type = OK; /* tell PM that we succeeded */ s = ipc_send(PM_PROC_NR, &mess); /* send synchronization message */ system_hz = sys_hz(); /* Subscribe to block and character driver events. */ s = ds_subscribe("drv\\.[bc]..\\..*", DSF_INITIAL | DSF_OVERWRITE); if (s != OK) panic("VFS: can't subscribe to driver events (%d)", s); /* Initialize worker threads */ worker_init(); /* Initialize global locks */ if (mthread_mutex_init(&bsf_lock, NULL) != 0) panic("VFS: couldn't initialize block special file lock"); init_dmap(); /* Initialize device table. */ /* Map all the services in the boot image. */ if ((s = sys_safecopyfrom(RS_PROC_NR, info->rproctab_gid, 0, (vir_bytes) rprocpub, sizeof(rprocpub))) != OK){ panic("sys_safecopyfrom failed: %d", s); } for (i = 0; i < NR_BOOT_PROCS; i++) { if (rprocpub[i].in_use) { if ((s = map_service(&rprocpub[i])) != OK) { panic("VFS: unable to map service: %d", s); } } } /* Initialize locks and initial values for all processes. */ for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { if (mutex_init(&rfp->fp_lock, NULL) != 0) panic("unable to initialize fproc lock"); rfp->fp_worker = NULL; #if LOCK_DEBUG rfp->fp_vp_rdlocks = 0; rfp->fp_vmnt_rdlocks = 0; #endif /* Initialize process directories. mount_fs will set them to the * correct values. */ for (i = 0; i < OPEN_MAX; i++) rfp->fp_filp[i] = NULL; rfp->fp_rd = NULL; rfp->fp_wd = NULL; } init_vnodes(); /* init vnodes */ init_vmnts(); /* init vmnt structures */ init_select(); /* init select() structures */ init_filps(); /* Init filp structures */ /* Mount PFS and initial file system root. */ worker_start(fproc_addr(VFS_PROC_NR), do_init_root, &mess /*unused*/, FALSE /*use_spare*/); return(OK); }