- if (ref_count == 0) {
- assert(wait_queue_empty(&semaphore->wait_queue));
- ipc_port_dealloc_kernel(semaphore->port);
- zfree(semaphore_zone, semaphore);
+ /*
+ * Last ref, clean up the port [if any]
+ * associated with the semaphore, destroy
+ * it (if still active) and then free
+ * the semaphore.
+ */
+ ipc_port_t port = semaphore->port;
+
+ if (IP_VALID(port)) {
+ assert(!port->ip_srights);
+ ipc_port_dealloc_kernel(port);
+ }
+
+ /*
+ * Lock the semaphore to lock in the owner task reference.
+ * Then continue to try to lock the task (inverse order).
+ */
+ spl_level = splsched();
+ semaphore_lock(semaphore);
+ for (collisions = 0; semaphore->active; collisions++) {
+ task_t task = semaphore->owner;
+
+ assert(task != TASK_NULL);
+
+ if (task_lock_try(task)) {
+ semaphore_destroy_internal(task, semaphore);
+ /* semaphore unlocked */
+ splx(spl_level);
+ task_unlock(task);
+ goto out;