1 module mecca.platform.os.darwin;
2 
3 version (Darwin):
4 package(mecca):
5 
6 public import mecca.platform.os.darwin.ucontext;
7 public import mecca.platform.os.darwin.time;
8 
9 import core.sys.posix.sys.types : pthread_t;
10 
11 import mecca.platform.os : MmapArguments;
12 
13 // These two do not exist on Darwin platforms. We'll just use a value that won't
14 // have any affect when used together with mmap and mremap.
15 enum MAP_POPULATE = 0;
16 enum MREMAP_MAYMOVE = 0;
17 
18 ///
19 enum OSSignal
20 {
21     SIGNONE = 0, /// invalid
22     SIGHUP = 1, /// hangup
23     SIGINT = 2, /// interrupt
24     SIGQUIT = 3, /// quit
25     SIGILL = 4, /// illegal instruction (not reset when caught)
26     SIGTRAP = 5, /// trace trap (not reset when caught)
27     SIGABRT = 6, /// abort()
28     SIGIOT = SIGABRT, /// compatibility
29     SIGEMT = 7, /// EMT instruction
30     SIGFPE = 8, /// floating point exception
31     SIGKILL = 9, /// kill (cannot be caught or ignored)
32     SIGBUS = 10, /// bus error
33     SIGSEGV = 11, /// segmentation violation
34     SIGSYS = 12, /// bad argument to system call
35     SIGPIPE = 13, /// write on a pipe with no one to read it
36     SIGALRM = 14, /// alarm clock
37     SIGTERM = 15, /// software termination signal from kill
38     SIGURG = 16, /// urgent condition on IO channel
39     SIGSTOP = 17, /// sendable stop signal not from tty
40     SIGTSTP = 18, /// stop signal from tty
41     SIGCONT = 19, /// continue a stopped process
42     SIGCHLD = 20, /// to parent on child stop or exit
43     SIGTTIN = 21, /// to readers pgrp upon background tty read
44     SIGTTOU = 22, /// like TTIN for output if (tp->t_local&LTOSTOP)
45     SIGIO = 23, /// input/output possible signal
46     SIGXCPU = 24, /// exceeded CPU time limit
47     SIGXFSZ = 25, /// exceeded file size limit
48     SIGVTALRM = 26, /// virtual time alarm
49     SIGPROF = 27, /// profiling time alarm
50     SIGWINCH = 28, /// window size changes
51     SIGINFO = 29, /// information request
52     SIGUSR1 = 30, /// user defined signal 1
53     SIGUSR2 = 31 /// user defined signal 2
54 }
55 
56 /**
57  * Represents the ID of a thread.
58  *
59  * This type is platform dependent.
60  */
61 alias ThreadId = ulong;
62 
63 __gshared static immutable BLOCKED_SIGNALS = [
64     OSSignal.SIGHUP, OSSignal.SIGINT, OSSignal.SIGQUIT,
65     //OSSignal.SIGILL, OSSignal.SIGTRAP, OSSignal.SIGABRT,
66     //OSSignal.SIGBUS, OSSignal.SIGFPE, OSSignal.SIGKILL,
67     //OSSignal.SIGUSR1, OSSignal.SIGSEGV, OSSignal.SIGUSR2,
68     OSSignal.SIGPIPE, OSSignal.SIGALRM, OSSignal.SIGTERM,
69     //OSSignal.SIGSTKFLT, OSSignal.SIGCONT, OSSignal.SIGSTOP,
70     OSSignal.SIGCHLD, OSSignal.SIGTSTP, OSSignal.SIGTTIN,
71     OSSignal.SIGTTOU, OSSignal.SIGURG, OSSignal.SIGXCPU,
72     OSSignal.SIGXFSZ, OSSignal.SIGVTALRM, OSSignal.SIGPROF,
73     OSSignal.SIGWINCH, OSSignal.SIGIO,
74     //OSSignal.SIGSYS,
75 ];
76 
77 extern (C) private int pthread_threadid_np(pthread_t, ulong*) nothrow;
78 
79 /// Returns: the current thread ID
80 ThreadId currentThreadId() @system nothrow
81 {
82     import mecca.lib.exception : ASSERT;
83 
84     enum assertMessage = "pthread_threadid_np failed, should not happen";
85 
86     ulong threadId;
87     ASSERT!"assertMessage"(pthread_threadid_np(null, &threadId) == 0);
88 
89     return threadId;
90 }
91 
92 enum O_CLOEXEC = 0x1000000;
93 enum F_DUPFD_CLOEXEC = 67;
94 
95 // this does not exist on Darwin
96 enum EREMOTEIO = -1;
97 
98 // `pipe2` does not exist on Darwin so we're emulating it instead. This is
99 // emulated by first calling the regular `pipe` followed by `fcntl` on the two
100 // file descriptors. This is not thread safe.
101 extern(C) private int pipe2(ref int[2] pipefd, int flags) nothrow @trusted @nogc
102 {
103     import core.sys.posix.unistd : close, pipe;
104 
105     static int setFlags(int fd, int flags)
106     {
107         import core.sys.posix.fcntl : fcntl, F_SETFD, F_GETFD;
108 
109         const existingFlags = fcntl(fd, F_GETFD);
110 
111         if (existingFlags == -1)
112             return existingFlags;
113 
114         return fcntl(fd, F_SETFD, existingFlags | flags);
115     }
116 
117     static void closePipe(ref int[2] pipe)
118     {
119         foreach (fd; pipe)
120             close(fd);
121     }
122 
123     const pipeResult = pipe(pipefd);
124 
125     if (pipeResult != 0)
126         return pipeResult;
127 
128     foreach (fd; pipefd)
129     {
130         if (setFlags(fd, flags) == -1)
131         {
132             closePipe(pipefd);
133             return -1;
134         }
135     }
136 
137     return 0;
138 }
139 
140 enum ITIMER_REAL = 0;
141 
142 void* mremap(MmapArguments mmapArguments, void* oldAddress, size_t oldSize,
143     size_t newSize, int flags, void* newAddress = null)
144 {
145     import core.stdc..string : memcpy;
146     import core.sys.posix.sys.mman : mmap, munmap, MAP_FAILED;
147 
148     if (oldSize == newSize)
149         return oldAddress;
150 
151     if (newSize < oldSize)
152     {
153         const sizeToUnmap = oldSize - newSize;
154         if (munmap(oldAddress + sizeToUnmap, sizeToUnmap) != 0)
155             return MAP_FAILED;
156 
157         return oldAddress;
158     }
159 
160     auto newMemory = mmap(newAddress, newSize, mmapArguments.tupleof);
161 
162     if (newMemory == MAP_FAILED)
163         return MAP_FAILED;
164 
165     memcpy(newMemory, oldAddress, oldSize);
166 
167     if (munmap(oldAddress, oldSize) != 0)
168     {
169         if (munmap(newMemory, newSize) != 0)
170             assert(false);
171 
172         return MAP_FAILED;
173     }
174 
175     return newMemory;
176 }