You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//alternative is QueryPerformanceCounter; it probably uses rdtsc, which is stable on recent processors, but it gives inconsistent results when the processor goes to sleep
Spyxorigthread; // thread number that is working on this pyx, or _1 if the value is available
177
177
Cerrcode; // 0 if no error, or error code
178
178
#ifPYXES
179
-
WAITBLOKpyxwb; // sync info
179
+
//WAITBLOK pyxwb; // sync info
180
+
UI4state;//one of the below pyx states. Monotonically increases
180
181
#endif
181
182
} PYXBLOK;
182
-
183
+
enum{
184
+
PYXEMPTY, //the pyx is not filled in, and no one is waiting
185
+
PYXWAIT, //at least 1 thread is waiting, and the pyx is not filled in
186
+
PYXFULL}; //the pyx is filled in
183
187
#ifPYXES
184
188
185
189
// Install a value/errcode into a (recursive) pyx, and broadcast to anyone waiting on it. fa() the pyx to indicate that the thread has released the pyx
@@ -191,7 +195,7 @@ static I jtsetpyxval(J jt, A pyx, A z, C errcode){I res=1;
191
195
if(likely(z!=0))ra(z); // since the pyx is recursive, we must ra the result we store into it. Could zap if inplaceable
192
196
__atomic_store_n(&((PYXBLOK*)AAV0(pyx))->pyxvalue,z,__ATOMIC_RELEASE); // set result value
193
197
// broadcast to wake up any tasks waiting for the result
// unprotect pyx. It was raised when it was assigned to this owner; now it belongs to the system
196
200
fa(pyx);
197
201
R1;
@@ -201,41 +205,33 @@ static I jtsetpyxval(J jt, A pyx, A z, C errcode){I res=1;
201
205
staticAjtcreatepyx(Jjt, Ithread,Dtimeout){Apyx;
202
206
// Allocate. Init value, cond, and mutex to idle
203
207
GAT0(pyx,INT,((sizeof(PYXBLOK)+(SZI-1))>>LGSZI)+1,0); AAV0(pyx)[0]=0; // allocate the result pointer (1), and the cond/mutex for the pyx.
204
-
WAITBLOKINIT(&((PYXBLOK*)AAV0(pyx))->pyxwb);
208
+
((PYXBLOK*)AAV0(pyx))->state=PYXEMPTY;
205
209
// Init the pyx to a recursive box, with raised usecount. AN=1 always. But set the value/errcode to NULL/no error and the thread# to the executing thread
// The pyx's usecount of 2 is one for the owning thread and one for the current thread, which has a tpop for the pyx that protects it until it is put into its box. When the pyx is filled in the owner will fa().
208
212
Rpyx;
209
213
}
210
214
211
215
// w is an A holding a pyx value. Return its value when it has been resolved. If it times out
212
-
Ajtpyxval(Jjt,Apyx){Ares; Cerrcode;
213
-
Dmaxtime=tod()+((PYXBLOK*)AAV0(pyx))->pyxmaxwt+0.000001; // get the time when we have to give up on this pyx, min 1usec
214
-
// read the pyx value. Since the creating thread has a release barrier after creation and another after final resolution, we can be sure
215
-
// that if we read nonzero the pyx has been resolved, even without an acquire barrier
216
-
while((res=__atomic_load_n(&((PYXBLOK*)AAV0(pyx))->pyxvalue,__ATOMIC_ACQUIRE))==0&&(errcode=__atomic_load_n(&((PYXBLOK*)AAV0(pyx))->errcode,__ATOMIC_ACQUIRE))==0){ // repeat till defined
// wait till the value is defined. We have to make one last check inside the lock to make sure the value is still unresolved
219
226
// The wait may time out because another thread is requesting a system lock. If so, we accept it now
220
227
if(unlikely(adbreak>>8)!=0){jtsystemlockaccept(jt,LOCKPRISYM+LOCKPRIPATH+LOCKPRIDEBUG); continue;} // process lock and keep waiting
221
228
// or, the user may be requesting a BREAK interrupt for deadlock or other slow execution. In that case fail the pyx. It will not be deleted until the value has been stored
222
-
if(unlikely((adbreak&0xff)>1)){errcode=EVBREAK; break;} // JBREAK: fail the pyx and exit
223
-
// if the pyx has a max time, see if that is exceeded
224
-
if(unlikely(maxtime<tod())){errcode=EVTIME; break;} // timeout: fail the pyx and exit
structtimespecendtime={nowtime.tv_usec+(tousec>=1000000),tousec-1000000*(tousec>=1000000)}; // system time when we give up. The struct says it uses nsec but it seems to use usec
R (w.tv_sec-t.tv_sec)*1000000000+w.tv_nsec-t.tv_nsec;}
30
+
9
31
#if defined(__APPLE__) || defined(__linux__)
10
32
enum{FREE=0,LOCK=1,WAIT=2};//values for mutex->v
11
33
//todo consider storing owner in the high bits of v. apple pthreads does this. But it means we can't use xadd to unlock. On the other hand, apple is mostly arm now, which doesn't have xadd anyway.
@@ -15,16 +37,17 @@ enum{FREE=0,LOCK=1,WAIT=2};//values for mutex->v
15
37
16
38
// there is a flaw. If t0 holds lock, t1 attempts to acquire it; when it eventually does, it will leave WAIT in v instead of LOCK,
17
39
18
-
// todo figure out ULF_WAIT_CANCEL_POINT (I think it allows implementing the desired behaviour for EVATTN)
UI4e;if(likely((!(e=lda(&m->v)))&&((e=FREE),casa(&m->v,&e,LOCK)))){m->ct+=m->recursive;m->owner=self;R0;}//success. test-and-test-and-set is from glibc, mildly optimises the case when many threads swarm a locked mutex
45
+
UI4e;if(likely((!(e=lda(&m->v)))&&((e=FREE),casa(&m->v,&e,LOCK))))goto success;//fast path. test-and-test-and-set is from glibc, mildly optimises the case when many threads swarm a locked mutex. Not sure if this is for the best, but after waffling for a bit I think it is
24
46
if(e!=WAIT)e=xchga(&m->v,WAIT); //penalise the multi-waiters case, since it's slower anyway
25
47
while(e!=FREE){
26
48
#if__linux__
27
-
Ii=_jfutex_waitn(&m->v,WAIT,(UI)-1); //bug? jfutex_wait doesn't get interrupted by signals on linux
49
+
Ii=_jfutex_waitn(&m->v,WAIT,(UI)-1);
50
+
//bug? futex wait doesn't get interrupted by signals on linux if timeout is null
28
51
#else
29
52
Ii=jfutex_wait(&m->v,WAIT);
30
53
#endif
@@ -34,12 +57,11 @@ C jtpthread_mutex_lock(J jt,jtpthread_mutex_t *m,I self){
34
57
elseif(i==-ENOMEM)REVWSFULL;//lol
35
58
elseREVFACE;}
36
59
e=xchga(&m->v,WAIT);} //exit when e==FREE; i.e., _we_ successfully installed WAIT in place of FREE
0 commit comments