public static int DoFutex(Thread current, ref Arch.ExceptionRegisters regs, UserPtr uaddr, int op, int val, UserPtr timeoutPtr, UserPtr uaddr2, uint val3) { var cmd = op & FUTEX_CMD_MASK; var flags = ToFlags(op, cmd); timespec ts; ts.tv_sec = ts.tv_nsec = 0; // Don't care about shared mutex if ((flags & FLAGS_SHARED) != 0) { return DoFutexShared(current, ref regs, uaddr, op, val, timeoutPtr, uaddr2, val3); } bool hasTimeout = timeoutPtr != UserPtr.Zero; if (hasTimeout && timeoutPtr.Read(current, out ts) != 0) return -ErrorCode.EFAULT; switch (cmd) { case FUTEX_WAIT: return Wait(current, ref regs, uaddr, flags, val, hasTimeout, ts, FUTEX_BITSET_MATCH_ANY); case FUTEX_WAIT_BITSET: return Wait(current, ref regs, uaddr, flags, val, hasTimeout, ts, val3); case FUTEX_WAKE: return Wake(current, uaddr, flags, val, FUTEX_BITSET_MATCH_ANY); case FUTEX_WAKE_BITSET: return Wake(current, uaddr, flags, val, val3); default: Arch.Console.Write("futex: unknown primitives "); Arch.Console.Write(cmd); Arch.Console.WriteLine(); return -ErrorCode.ENOSYS; } }
// Forward the futex request to Linux helper private static int DoFutexShared(Thread current, ref Arch.ExceptionRegisters regs, UserPtr uaddr, int op, int val, UserPtr timeoutPtr, UserPtr uaddr2, uint val3) { // Some local test var cmd = op & FUTEX_CMD_MASK; timespec ts = new timespec(); bool hasTimeout = timeoutPtr != UserPtr.Zero; if (hasTimeout && timeoutPtr.Read(current, out ts) != 0) return -ErrorCode.EFAULT; if (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET) { int old_val; if (uaddr.Read(current, out old_val) != 0) return -ErrorCode.EFAULT; if (old_val != val) return -ErrorCode.EWOULDBLOCK; var bitset = cmd == FUTEX_WAIT ? FUTEX_BITSET_MATCH_ANY : val3; var shadowAddr = FindShadowAddr(current, uaddr); if (shadowAddr == Pointer.Zero) { Arch.Console.WriteLine("FutexShared: Don't know how to deal with shared_wait"); return 0; } var futex_entry = new FutexCompletionEntry(current, uaddr, bitset); Arch.IPCStubs.linux_sys_futex_wait(current.Parent.helperPid, current.impl._value.thread._value, op, shadowAddr, val, ts, bitset); Globals.CompletionQueue.Enqueue(futex_entry); current.SaveState(ref regs); current.AsyncReturn = true; return 0; } else if (cmd == FUTEX_WAKE || cmd == FUTEX_WAKE_BITSET) { var bitset = cmd == FUTEX_WAKE ? FUTEX_BITSET_MATCH_ANY : val3; if (bitset == 0) return -ErrorCode.EINVAL; var shadowAddr = FindShadowAddr(current, uaddr); if (shadowAddr == Pointer.Zero) { Arch.Console.WriteLine("FutexShared: Don't know how to deal with shared_wake"); return 0; } var c = new BridgeCompletion(current, new ByteBufferRef()); Arch.IPCStubs.linux_sys_futex_wake(current.Parent.helperPid, current.impl._value.thread._value, op, shadowAddr, bitset); Globals.CompletionQueue.Enqueue(c); current.SaveState(ref regs); current.AsyncReturn = true; return 0; } return 0; }
private static int Wait(Thread current, ref Arch.ExceptionRegisters regs, UserPtr uaddr, int flags, int val, bool hasTimeout, timespec ts, uint bitset) { int old_val; if (uaddr.Read(current, out old_val) != 0) return -ErrorCode.EFAULT; if (old_val != val) return -ErrorCode.EWOULDBLOCK; //Arch.Console.Write("wait: addr="); //Arch.Console.Write(uaddr.Value.ToUInt32()); //Arch.Console.Write(" thr="); //Arch.Console.Write(current.Tid); //Arch.Console.WriteLine(); TimerQueueNode node; var futex_entry = new FutexCompletionEntry(current, uaddr, bitset); Globals.FutexLists.InsertAtTail(futex_entry); if (hasTimeout) { node = Globals.TimeoutQueue.Enqueue(ts.ToMilliseconds(), current); futex_entry.timeoutNode = node; } Globals.CompletionQueue.Enqueue(futex_entry); current.SaveState(ref regs); current.AsyncReturn = true; return 0; }