/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
- *
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* Please see the License for the specific language governing rights and
* limitations under the License.
*
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
* Contains RT distributed lock synchronization services.
*/
-#include <kern/etap_macros.h>
+#include <mach/mach_types.h>
+#include <mach/lock_set_server.h>
+#include <mach/task_server.h>
+
#include <kern/misc_protos.h>
+#include <kern/kalloc.h>
#include <kern/sync_lock.h>
#include <kern/sched_prim.h>
#include <kern/ipc_kobject.h>
#include <kern/ipc_sync.h>
-#include <kern/etap_macros.h>
#include <kern/thread.h>
#include <kern/task.h>
#define ulock_ownership_set(ul, th) \
MACRO_BEGIN \
- thread_act_t _th_act; \
- _th_act = (th)->top_act; \
- act_lock(_th_act); \
- enqueue (&_th_act->held_ulocks, (queue_entry_t) (ul)); \
- act_unlock(_th_act); \
- (ul)->holder = _th_act; \
+ thread_mtx_lock(th); \
+ enqueue (&th->held_ulocks, (queue_entry_t) (ul)); \
+ thread_mtx_unlock(th); \
+ (ul)->holder = th; \
MACRO_END
#define ulock_ownership_clear(ul) \
MACRO_BEGIN \
- thread_act_t _th_act; \
- _th_act = (ul)->holder; \
- if (_th_act->active) { \
- act_lock(_th_act); \
- remqueue(&_th_act->held_ulocks, \
+ thread_t th; \
+ th = (ul)->holder; \
+ if ((th)->active) { \
+ thread_mtx_lock(th); \
+ remqueue(&th->held_ulocks, \
(queue_entry_t) (ul)); \
- act_unlock(_th_act); \
+ thread_mtx_unlock(th); \
} else { \
- remqueue(&_th_act->held_ulocks, \
+ remqueue(&th->held_ulocks, \
(queue_entry_t) (ul)); \
} \
- (ul)->holder = THR_ACT_NULL; \
+ (ul)->holder = THREAD_NULL; \
MACRO_END
/*
MACRO_END
unsigned int lock_set_event;
-#define LOCK_SET_EVENT ((event64_t)&lock_set_event)
+#define LOCK_SET_EVENT CAST_EVENT64_T(&lock_set_event)
unsigned int lock_set_handoff;
-#define LOCK_SET_HANDOFF ((event64_t)&lock_set_handoff)
+#define LOCK_SET_HANDOFF CAST_EVENT64_T(&lock_set_handoff)
+
+
+lck_attr_t lock_set_attr;
+lck_grp_t lock_set_grp;
+static lck_grp_attr_t lock_set_grp_attr;
+
+
/*
* ROUTINE: lock_set_init [private]
*
* Initialize the lock_set subsystem.
- *
- * For now, we don't have anything to do here.
*/
void
lock_set_init(void)
{
- return;
+ lck_grp_attr_setdefault(&lock_set_grp_attr);
+ lck_grp_init(&lock_set_grp, "lock_set", &lock_set_grp_attr);
+ lck_attr_setdefault(&lock_set_attr);
}
{
lock_set_t lock_set = LOCK_SET_NULL;
ulock_t ulock;
- int size;
+ vm_size_t size;
int x;
*new_lock_set = LOCK_SET_NULL;
if (task == TASK_NULL || n_ulocks <= 0 || policy > SYNC_POLICY_MAX)
return KERN_INVALID_ARGUMENT;
+ if ((VM_MAX_ADDRESS - sizeof(struct lock_set))/sizeof(struct ulock) < (unsigned)n_ulocks)
+ return KERN_RESOURCE_SHORTAGE;
+
size = sizeof(struct lock_set) + (sizeof(struct ulock) * (n_ulocks-1));
lock_set = (lock_set_t) kalloc (size);
lock_set_lock_init(lock_set);
lock_set->n_ulocks = n_ulocks;
- lock_set->ref_count = 1;
+ lock_set->ref_count = (task == kernel_task) ? 1 : 2; /* one for kernel, one for port */
/*
* Create and initialize the lock set port
*/
lock_set->port = ipc_port_alloc_kernel();
if (lock_set->port == IP_NULL) {
- /* This will deallocate the lock set */
- lock_set_dereference(lock_set);
+ kfree(lock_set, size);
return KERN_RESOURCE_SHORTAGE;
}
ulock = (ulock_t) &lock_set->ulock_list[x];
ulock_lock_init(ulock);
ulock->lock_set = lock_set;
- ulock->holder = THR_ACT_NULL;
+ ulock->holder = THREAD_NULL;
ulock->blocked = FALSE;
ulock->unstable = FALSE;
ulock->ho_wait = FALSE;
+ ulock->accept_wait = FALSE;
wait_queue_init(&ulock->wait_queue, policy);
}
kern_return_t
lock_set_destroy (task_t task, lock_set_t lock_set)
{
- thread_t thread;
ulock_t ulock;
int i;
lock_set_ownership_clear(lock_set, task);
/*
- * Deallocate
- *
- * Drop the lock set reference, which inturn destroys the
- * lock set structure if the reference count goes to zero.
+ * Drop the lock set reference given to the containing task,
+ * which inturn destroys the lock set structure if the reference
+ * count goes to zero.
*/
-
- ipc_port_dealloc_kernel(lock_set->port);
lock_set_dereference(lock_set);
return KERN_SUCCESS;
* Block the current thread if the lock is already held.
*/
- if (ulock->holder != THR_ACT_NULL) {
+ if (ulock->holder != THREAD_NULL) {
int wait_result;
- if (ulock->holder == current_act()) {
+ if (ulock->holder == current_thread()) {
ulock_unlock(ulock);
return KERN_LOCK_OWNED_SELF;
}
ulock->blocked = TRUE;
wait_result = wait_queue_assert_wait64(&ulock->wait_queue,
LOCK_SET_EVENT,
- THREAD_ABORTSAFE);
+ THREAD_ABORTSAFE, 0);
ulock_unlock(ulock);
/*
ulock = (ulock_t) &lock_set->ulock_list[lock_id];
- return (lock_release_internal(ulock, current_act()));
+ return (ulock_release_internal(ulock, current_thread()));
}
kern_return_t
* whether it already holds the lock or another thread does.
*/
- if (ulock->holder != THR_ACT_NULL) {
+ if (ulock->holder != THREAD_NULL) {
lock_set_unlock(lock_set);
- if (ulock->holder == current_act()) {
+ if (ulock->holder == current_thread()) {
ulock_unlock(ulock);
return KERN_LOCK_OWNED_SELF;
}
ulock_lock(ulock);
lock_set_unlock(lock_set);
- if (ulock->holder != current_act()) {
+ if (ulock->holder != current_thread()) {
ulock_unlock(ulock);
return KERN_INVALID_RIGHT;
}
* KERN_LOCK_UNSTABLE status, until the lock is made stable again.
*/
kern_return_t
-lock_make_unstable (ulock_t ulock, thread_act_t thr_act)
+lock_make_unstable (ulock_t ulock, thread_t thread)
{
lock_set_t lock_set;
-
lock_set = ulock->lock_set;
lock_set_lock(lock_set);
if (!lock_set->active) {
ulock_lock(ulock);
lock_set_unlock(lock_set);
- if (ulock->holder != thr_act) {
+ if (ulock->holder != thread) {
ulock_unlock(ulock);
return KERN_INVALID_RIGHT;
}
}
/*
- * ROUTINE: lock_release_internal [internal]
+ * ROUTINE: ulock_release_internal [internal]
*
* Releases the ulock.
* If any threads are blocked waiting for the ulock, one is woken-up.
*
*/
kern_return_t
-lock_release_internal (ulock_t ulock, thread_act_t thr_act)
+ulock_release_internal (ulock_t ulock, thread_t thread)
{
lock_set_t lock_set;
- int result;
-
if ((lock_set = ulock->lock_set) == LOCK_SET_NULL)
return KERN_INVALID_ARGUMENT;
ulock_lock(ulock);
lock_set_unlock(lock_set);
- if (ulock->holder != thr_act) {
+ if (ulock->holder != thread) {
ulock_unlock(ulock);
return KERN_INVALID_RIGHT;
}
*/
if (ulock->blocked) {
wait_queue_t wq = &ulock->wait_queue;
- thread_t thread;
+ thread_t wqthread;
spl_t s;
s = splsched();
wait_queue_lock(wq);
- thread = wait_queue_wakeup64_identity_locked(wq,
+ wqthread = wait_queue_wakeup64_identity_locked(wq,
LOCK_SET_EVENT,
THREAD_AWAKENED,
TRUE);
/* wait_queue now unlocked, thread locked */
- if (thread != THREAD_NULL) {
- /*
- * JMM - These ownership transfer macros have a
- * locking/race problem. To keep the thread from
- * changing states on us (nullifying the ownership
- * assignment) we need to keep the thread locked
- * during the assignment. But we can't because the
- * macros take an activation lock, which is a mutex.
- * Since this code was already broken before I got
- * here, I will leave it for now.
- */
- thread_unlock(thread);
+ if (wqthread != THREAD_NULL) {
+ thread_unlock(wqthread);
splx(s);
/*
* from the current thread to the acquisition thread.
*/
ulock_ownership_clear(ulock);
- ulock_ownership_set(ulock, thread);
+ ulock_ownership_set(ulock, wqthread);
ulock_unlock(ulock);
return KERN_SUCCESS;
ulock_lock(ulock);
lock_set_unlock(lock_set);
- if (ulock->holder != current_act()) {
+ if (ulock->holder != current_thread()) {
ulock_unlock(ulock);
return KERN_INVALID_RIGHT;
}
* Transfer lock ownership
*/
if (thread != THREAD_NULL) {
- /*
- * JMM - These ownership transfer macros have a
- * locking/race problem. To keep the thread from
- * changing states on us (nullifying the ownership
- * assignment) we need to keep the thread locked
- * during the assignment. But we can't because the
- * macros take an activation lock, which is a mutex.
- * Since this code was already broken before I got
- * here, I will leave it for now.
+ /*
+ * The thread we are transferring to will try
+ * to take the lock on the ulock, and therefore
+ * will wait for us complete the handoff even
+ * through we set the thread running.
*/
thread_unlock(thread);
splx(s);
ulock->ho_wait = TRUE;
wait_result = wait_queue_assert_wait64(&ulock->wait_queue,
LOCK_SET_HANDOFF,
- THREAD_ABORTSAFE);
+ THREAD_ABORTSAFE, 0);
ulock_unlock(ulock);
if (wait_result == THREAD_WAITING)
*/
switch (wait_result) {
+
case THREAD_AWAKENED:
+ /*
+ * we take the ulock lock to syncronize with the
+ * thread that is accepting ownership.
+ */
+ ulock_lock(ulock);
+ assert(ulock->holder != current_thread());
+ ulock_unlock(ulock);
return KERN_SUCCESS;
case THREAD_INTERRUPTED:
ulock_lock(ulock);
- assert(ulock->holder == current_act());
+ assert(ulock->holder == current_thread());
ulock->ho_wait = FALSE;
ulock_unlock(ulock);
return KERN_ABORTED;
case THREAD_RESTART:
goto retry;
-
- default:
- panic("lock_handoff");
}
+
+ panic("lock_handoff");
+ return KERN_FAILURE;
}
kern_return_t
return KERN_ALREADY_WAITING;
}
- if (ulock->holder == current_act()) {
+ if (ulock->holder == current_thread()) {
ulock_unlock(ulock);
return KERN_LOCK_OWNED_SELF;
}
*/
if (ulock->ho_wait) {
wait_queue_t wq = &ulock->wait_queue;
- thread_t thread;
/*
* See who the lucky devil is, if he is still there waiting.
*/
- assert(ulock->holder != THR_ACT_NULL);
- thread = ulock->holder->thread;
+ assert(ulock->holder != THREAD_NULL);
if (wait_queue_wakeup64_thread(wq,
LOCK_SET_HANDOFF,
- thread,
+ ulock->holder,
THREAD_AWAKENED) == KERN_SUCCESS) {
/*
* Holder thread was still waiting to give it
ulock->accept_wait = TRUE;
wait_result = wait_queue_assert_wait64(&ulock->wait_queue,
LOCK_SET_HANDOFF,
- THREAD_ABORTSAFE);
+ THREAD_ABORTSAFE, 0);
ulock_unlock(ulock);
if (wait_result == THREAD_WAITING)
switch (wait_result) {
case THREAD_AWAKENED:
+ /*
+ * Take the lock to synchronize with the thread handing
+ * off the lock to us. We don't want to continue until
+ * they complete the handoff.
+ */
+ ulock_lock(ulock);
+ assert(ulock->accept_wait == FALSE);
+ assert(ulock->holder == current_thread());
+ ulock_unlock(ulock);
return KERN_SUCCESS;
case THREAD_INTERRUPTED:
case THREAD_RESTART:
goto retry;
-
- default:
- panic("lock_handoff_accept");
}
+
+ panic("lock_handoff_accept");
+ return KERN_FAILURE;
}
/*
lock_set_unlock(lock_set);
if (ref_count == 0) {
- size = sizeof(struct lock_set) +
- (sizeof(struct ulock) * (lock_set->n_ulocks - 1));
- kfree((vm_offset_t) lock_set, size);
+ ipc_port_dealloc_kernel(lock_set->port);
+ size = (int)(sizeof(struct lock_set) +
+ (sizeof(struct ulock) * (lock_set->n_ulocks - 1)));
+ kfree(lock_set, size);
+ }
+}
+
+void
+ulock_release_all(
+ thread_t thread)
+{
+ ulock_t ulock;
+
+ while (!queue_empty(&thread->held_ulocks)) {
+ ulock = (ulock_t)queue_first(&thread->held_ulocks);
+ lock_make_unstable(ulock, thread);
+ ulock_release_internal(ulock, thread);
}
}