/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _FAKE_KERNEL #define THR_TO_DID(t) ((kt_did_t)(uintptr_t)t) #else #define THR_TO_DID(t) (t->t_did) #endif static boolean_t smb_thread_continue_timedwait_locked(smb_thread_t *, int); /* * smb_thread_entry_point * * Common entry point for all the threads created through smb_thread_start. * The state of the thread is set to "running" at the beginning and moved to * "exiting" just before calling thread_exit(). The condition variable is * also signaled. */ static void smb_thread_entry_point( smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); ASSERT(thread->sth_state == SMB_THREAD_STATE_STARTING); if (!thread->sth_kill) { thread->sth_state = SMB_THREAD_STATE_RUNNING; cv_signal(&thread->sth_cv); mutex_exit(&thread->sth_mtx); /* Run the real thread entry point. */ thread->sth_ep(thread, thread->sth_ep_arg); mutex_enter(&thread->sth_mtx); } /* * It's tempting to clear sth_did here too, but don't. * That's needed in thread_join(). */ thread->sth_th = NULL; thread->sth_state = SMB_THREAD_STATE_EXITING; cv_broadcast(&thread->sth_cv); mutex_exit(&thread->sth_mtx); zthread_exit(); } /* * smb_thread_init */ void smb_thread_init( smb_thread_t *thread, char *name, smb_thread_ep_t ep, void *ep_arg, pri_t pri) { ASSERT(thread->sth_magic != SMB_THREAD_MAGIC); bzero(thread, sizeof (*thread)); (void) strlcpy(thread->sth_name, name, sizeof (thread->sth_name)); thread->sth_ep = ep; thread->sth_ep_arg = ep_arg; thread->sth_state = SMB_THREAD_STATE_EXITED; thread->sth_pri = pri; mutex_init(&thread->sth_mtx, NULL, MUTEX_DEFAULT, NULL); cv_init(&thread->sth_cv, NULL, CV_DEFAULT, NULL); thread->sth_magic = SMB_THREAD_MAGIC; } /* * smb_thread_destroy */ void smb_thread_destroy( smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); ASSERT(thread->sth_state == SMB_THREAD_STATE_EXITED); thread->sth_magic = 0; mutex_destroy(&thread->sth_mtx); cv_destroy(&thread->sth_cv); } /* * smb_thread_start * * This function starts a thread with the parameters provided. It waits until * the state of the thread has been moved to running. */ /*ARGSUSED*/ int smb_thread_start( smb_thread_t *thread) { int rc = 0; kthread_t *tmpthread; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); switch (thread->sth_state) { case SMB_THREAD_STATE_EXITED: thread->sth_state = SMB_THREAD_STATE_STARTING; mutex_exit(&thread->sth_mtx); tmpthread = zthread_create(NULL, 0, smb_thread_entry_point, thread, 0, thread->sth_pri); ASSERT(tmpthread != NULL); mutex_enter(&thread->sth_mtx); thread->sth_th = tmpthread; thread->sth_did = THR_TO_DID(tmpthread); while (thread->sth_state == SMB_THREAD_STATE_STARTING) cv_wait(&thread->sth_cv, &thread->sth_mtx); if (thread->sth_state != SMB_THREAD_STATE_RUNNING) rc = -1; break; default: ASSERT(0); rc = -1; break; } mutex_exit(&thread->sth_mtx); return (rc); } /* * smb_thread_stop * * This function signals a thread to kill itself and waits until the "exiting" * state has been reached. */ void smb_thread_stop(smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); switch (thread->sth_state) { case SMB_THREAD_STATE_RUNNING: case SMB_THREAD_STATE_STARTING: if (!thread->sth_kill) { thread->sth_kill = B_TRUE; cv_broadcast(&thread->sth_cv); while (thread->sth_state != SMB_THREAD_STATE_EXITING) cv_wait(&thread->sth_cv, &thread->sth_mtx); mutex_exit(&thread->sth_mtx); thread_join(thread->sth_did); mutex_enter(&thread->sth_mtx); thread->sth_state = SMB_THREAD_STATE_EXITED; thread->sth_did = 0; thread->sth_kill = B_FALSE; cv_broadcast(&thread->sth_cv); break; } /* FALLTHROUGH */ case SMB_THREAD_STATE_EXITING: if (thread->sth_kill) { while (thread->sth_state != SMB_THREAD_STATE_EXITED) cv_wait(&thread->sth_cv, &thread->sth_mtx); } else { thread->sth_state = SMB_THREAD_STATE_EXITED; thread->sth_did = 0; } break; case SMB_THREAD_STATE_EXITED: break; default: ASSERT(0); break; } mutex_exit(&thread->sth_mtx); } /* * smb_thread_signal * * This function signals a thread. */ void smb_thread_signal(smb_thread_t *thread) { ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); switch (thread->sth_state) { case SMB_THREAD_STATE_RUNNING: cv_signal(&thread->sth_cv); break; default: break; } mutex_exit(&thread->sth_mtx); } boolean_t smb_thread_continue(smb_thread_t *thread) { boolean_t result; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); result = smb_thread_continue_timedwait_locked(thread, 0); mutex_exit(&thread->sth_mtx); return (result); } boolean_t smb_thread_continue_nowait(smb_thread_t *thread) { boolean_t result; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); /* * Setting ticks=-1 requests a non-blocking check. We will * still block if the thread is in "suspend" state. */ result = smb_thread_continue_timedwait_locked(thread, -1); mutex_exit(&thread->sth_mtx); return (result); } boolean_t smb_thread_continue_timedwait(smb_thread_t *thread, int seconds) { boolean_t result; ASSERT(thread->sth_magic == SMB_THREAD_MAGIC); mutex_enter(&thread->sth_mtx); result = smb_thread_continue_timedwait_locked(thread, SEC_TO_TICK(seconds)); mutex_exit(&thread->sth_mtx); return (result); } /* * smb_thread_continue_timedwait_locked * * Internal only. Ticks==-1 means don't block, Ticks == 0 means wait * indefinitely */ static boolean_t smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks) { boolean_t result; /* -1 means don't block */ if (ticks != -1 && !thread->sth_kill) { if (ticks == 0) { cv_wait(&thread->sth_cv, &thread->sth_mtx); } else { (void) cv_reltimedwait(&thread->sth_cv, &thread->sth_mtx, (clock_t)ticks, TR_CLOCK_TICK); } } result = (thread->sth_kill == 0); return (result); }