+/* always called under uipc_lock */
+static void
+unp_gc_wait(void)
+{
+ if (unp_gcthread == current_thread()) {
+ return;
+ }
+
+ while (unp_gcing != 0) {
+ unp_gcwait = 1;
+ msleep(&unp_gcing, &uipc_lock, 0, "unp_gc_wait", NULL);
+ }
+}
+
+/*
+ * fg_insertuipc_mark
+ *
+ * Description: Mark fileglob for insertion onto message queue if needed
+ * Also takes fileglob reference
+ *
+ * Parameters: fg Fileglob pointer to insert
+ *
+ * Returns: true, if the fileglob needs to be inserted onto msg queue
+ *
+ * Locks: Takes and drops fg_lock, potentially many times
+ */
+static boolean_t
+fg_insertuipc_mark(struct fileglob * fg)
+{
+ boolean_t insert = FALSE;
+
+ lck_mtx_lock_spin(&fg->fg_lock);
+ while (fg->fg_lflags & FG_RMMSGQ) {
+ lck_mtx_convert_spin(&fg->fg_lock);
+
+ fg->fg_lflags |= FG_WRMMSGQ;
+ msleep(&fg->fg_lflags, &fg->fg_lock, 0, "fg_insertuipc", NULL);
+ }
+
+ os_ref_retain_raw(&fg->fg_count, &f_refgrp);
+ fg->fg_msgcount++;
+ if (fg->fg_msgcount == 1) {
+ fg->fg_lflags |= FG_INSMSGQ;
+ insert = TRUE;
+ }
+ lck_mtx_unlock(&fg->fg_lock);
+ return insert;
+}
+
+/*
+ * fg_insertuipc
+ *
+ * Description: Insert marked fileglob onto message queue
+ *
+ * Parameters: fg Fileglob pointer to insert
+ *
+ * Returns: void
+ *
+ * Locks: Takes and drops fg_lock & uipc_lock
+ * DO NOT call this function with proc_fdlock held as unp_gc()
+ * can potentially try to acquire proc_fdlock, which can result
+ * in a deadlock if this function is in unp_gc_wait().
+ */
+static void
+fg_insertuipc(struct fileglob * fg)
+{
+ if (fg->fg_lflags & FG_INSMSGQ) {
+ lck_mtx_lock_spin(&uipc_lock);
+ unp_gc_wait();
+ LIST_INSERT_HEAD(&unp_msghead, fg, f_msglist);
+ lck_mtx_unlock(&uipc_lock);
+ lck_mtx_lock(&fg->fg_lock);
+ fg->fg_lflags &= ~FG_INSMSGQ;
+ if (fg->fg_lflags & FG_WINSMSGQ) {
+ fg->fg_lflags &= ~FG_WINSMSGQ;
+ wakeup(&fg->fg_lflags);
+ }
+ lck_mtx_unlock(&fg->fg_lock);
+ }
+}
+
+/*
+ * fg_removeuipc_mark
+ *
+ * Description: Mark the fileglob for removal from message queue if needed
+ * Also releases fileglob message queue reference
+ *
+ * Parameters: fg Fileglob pointer to remove
+ *
+ * Returns: true, if the fileglob needs to be removed from msg queue
+ *
+ * Locks: Takes and drops fg_lock, potentially many times
+ */
+static boolean_t
+fg_removeuipc_mark(struct fileglob * fg)
+{
+ boolean_t remove = FALSE;
+
+ lck_mtx_lock_spin(&fg->fg_lock);
+ while (fg->fg_lflags & FG_INSMSGQ) {
+ lck_mtx_convert_spin(&fg->fg_lock);
+
+ fg->fg_lflags |= FG_WINSMSGQ;
+ msleep(&fg->fg_lflags, &fg->fg_lock, 0, "fg_removeuipc", NULL);
+ }
+ fg->fg_msgcount--;
+ if (fg->fg_msgcount == 0) {
+ fg->fg_lflags |= FG_RMMSGQ;
+ remove = TRUE;
+ }
+ lck_mtx_unlock(&fg->fg_lock);
+ return remove;
+}
+
+/*
+ * fg_removeuipc
+ *
+ * Description: Remove marked fileglob from message queue
+ *
+ * Parameters: fg Fileglob pointer to remove
+ *
+ * Returns: void
+ *
+ * Locks: Takes and drops fg_lock & uipc_lock
+ * DO NOT call this function with proc_fdlock held as unp_gc()
+ * can potentially try to acquire proc_fdlock, which can result
+ * in a deadlock if this function is in unp_gc_wait().
+ */
+static void
+fg_removeuipc(struct fileglob * fg)
+{
+ if (fg->fg_lflags & FG_RMMSGQ) {
+ lck_mtx_lock_spin(&uipc_lock);
+ unp_gc_wait();
+ LIST_REMOVE(fg, f_msglist);
+ lck_mtx_unlock(&uipc_lock);
+ lck_mtx_lock(&fg->fg_lock);
+ fg->fg_lflags &= ~FG_RMMSGQ;
+ if (fg->fg_lflags & FG_WRMMSGQ) {
+ fg->fg_lflags &= ~FG_WRMMSGQ;
+ wakeup(&fg->fg_lflags);
+ }
+ lck_mtx_unlock(&fg->fg_lock);
+ }
+}
+