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
Copy file name to clipboardExpand all lines: jsrc/cx.c
+4-4Lines changed: 4 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -65,7 +65,7 @@
65
65
// popstmt is used to call tpop if the tpop stack is not empty; asgstmt is issued after the parse, where a variable can be invalidated to prevent it from being saved over calls
66
66
// we call only when the stack has values on it. We do our best during assignment to retrace the stack rather than zap it. If the stack has any values, we must call to make
67
67
// sure that all assignments-in-place have had the usercount fully restored
68
-
#defineparseline(z,popstmt,asgstmt) {tcesx=CWTCESX2(cwsent,ic); if((UI)jt->tnextpushp!=(UI)old){popstmt} S attnval=__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE); A *queue=&cwsent[(tcesx>>32)&TCESXSXMSK]; I m=(tcesx-(tcesx>>32))&TCESXSXMSK; \
68
+
#defineparseline(z,popstmt,asgstmt) {tcesx=CWTCESX2(cwsent,ic); popstmt S attnval=__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE); A *queue=&cwsent[(tcesx>>32)&TCESXSXMSK]; I m=(tcesx-(tcesx>>32))&TCESXSXMSK; \
// an incumbent value, we remove it. We also zap the value we install, just as in any normal assignment
126
126
L*asym=&SYMORIGIN[cv->indexsym]; // pointer symbol-table entry, index then item
127
127
ASSERT(!(asym->flag&LREADONLY),EVRO) // it had better not be readonly now
128
-
fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it
128
+
if(unlikely(QCWORD(asym->fval)!=0))fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it
129
129
Axx; GAT0(xx,INT,1,0); IAV0(xx)[0]=-1; AFLAGINIT(xx,AFRO) // -1 is the iteration number if there are no iterations; mark value RO to prevent xxx_index =: xxx_index + 1 from changing inplace
130
130
ACINITUNPUSH(xx); asym->fval=SETNAMED(MAKEFVAL(xx,ATYPETOVALTYPE(INT))); // raise usecount, as local name; install as value of xyz_index
131
131
rifv(t); // it would be work to handle virtual t, because you can't just ra() a virtual, as virtuals are freed only from the tpop stack. So we wimp out & realize. note we can free from a boxed array now
#defineCCOMMON(x,pref,err) ({A res=(x); pref if(unlikely((AT(res)&PYX)!=0)){if(unlikely((res=jtpyxval(jt,res))==0))err;} res; }) // extract & resolve contents; execute err if error in resolution x may have side effects
1025
1025
// since most uses of contents check the type first, it would be good to have the type loaded at any finish. But the compiler doesn't do that.
Copy file name to clipboardExpand all lines: jsrc/ja.h
+3-3Lines changed: 3 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -358,11 +358,11 @@
358
358
// faifowed does the free only if the block is marked FAOWED in stkf. These may have been stacked by local names, if the stack was later protected
359
359
#definefaifowed(x,Zc,tt,stkf) {if(unlikely(tt<0) || ((((Zc>>(ACPERMANENTX-(STKFAOWEDX+1)))&(4*STKFAOWED-1))<((I)stkf&STKFAOWED)) && unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2)))jtfamf(jt,x,tt);} // call if sparse or ending; never touch a PERM
360
360
// FAOWED becomes ..0f0; perm becomes .0p00 where perhaps p00 has +-1 added; 0f0>p00 means 'FAOWED & not PERMANENT'
#definefa(x) fajt(jt,(x)) // when the block will usually NOT be deleted
364
364
#definefalikely(x) fa(x) // when the block will usually be deleted (not used yet)
365
-
#definefatype(x,tt) {if((x)==0)SEGFAULT; if(likely(AC(x)<=ACUC1)){jtfamf(jt,x,tt);}else{if(likely(!ACISPERM(AC(x)))){if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,tt);}}} // scaf when type is known (i. e. NAME)
365
+
#definefatype(x,tt) {if(likely(AC(x)<=ACUC1)){jtfamf(jt,x,tt);}else{if(likely(!ACISPERM(AC(x)))){if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,tt);}}} // when type is known (i. e. NAME)
366
366
#definefanamedacv(x) {I Zc=AC(x); if(likely(!ACISPERM(Zc)))if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,AT(x));} // block is known to be ACV, recursive, AC>0, and almost always AC>1
367
367
// when x is known to be valid and usecount has gone to 0
#definerapos(x,sv) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(ISSPARSE(AT(x))))sv=jtra((x),SPARSE,sv);}} // better a misbranch than an atomic instruction if c<0
924
924
#defineraposlocal(x,sv) {I c=AC(x); if(likely(!ACISPERM(c))){if(c==1)AC(x)=ACUC2;else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(ISSPARSE(AT(x))))sv=jtra((x),SPARSE,sv);}} // better a misbranch than an atomic instruction if c<0
925
925
#defineraposacv(x) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL);}} // ACV is guaranteed recursive
926
-
#defineraposgblqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){ACADD(x,1); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse
926
+
#defineraposgblqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse
927
927
#defineraposlocalqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){if(c==1)AC(x)=ACUC2;else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse
928
928
#definerarecur(x) {I c=AC(x); if(likely(!ACISPERM(c))){if(c<0)AC(x)=(I)((UI)c+(ACINPLACE+ACUC1));else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); I tt=AT(x); if(unlikely(ISSPARSE(tt)))x=jtra((x),(tt),x);}} // ra but recursibles know to be recursive
929
929
// NOTE that every() produces blocks with usecount 0x8..2 (if a recursive block has pristine contents whose usecount is 2); if we ZAP that it must go to 2
Copy file name to clipboardExpand all lines: jsrc/p.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1060,7 +1060,7 @@ rejectfrag:;
1060
1060
// by another thread while we are using the value. The case is very rare but we test for it, so we have to make it work.
1061
1061
// If final assignment was local this can't happen, and we do the fa
1062
1062
jt->parserstackframe=oframe; // pop the parser frame-stack before tpushna, which may fail
1063
-
if(unlikely(ISSTKFAOWED(z))){if(pt0ecam&JTASGNWASLOCAL){faowed(QCWORD(z),AC(QCWORD(z)),AT(QCWORD(z)))} elsetpushna(QCWORD(z));} // if the result needs a free, do it, possibly deferred via tpush
1063
+
if(unlikely(ISSTKFAOWED(z))){if(pt0ecam&JTASGNWASLOCAL){faowed(QCWORD(z),AC(QCWORD(z)),AT(QCWORD(z)))} elsetpushna(QCWORD(z));} // if the result needs a free, do it, possibly deferred via tpush
1064
1064
}else{ // If there was an error during execution or name-stacking, exit with failure. Error has already been signaled. Remove zombiesym. Repurpose pt0ecam
1065
1065
failparsestack: // here we encountered an error during stacking. The error was processed using an old stack, so its spacing is wrong.
1066
1066
// we set the error word# for the failing word and then resignal the error to get the spacing right and call eformat to annotate it
Copy file name to clipboardExpand all lines: jsrc/s.c
+13-8Lines changed: 13 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -691,7 +691,7 @@ A jtprobequiet(J jt,A a){A g;
691
691
692
692
// assign symbol: assign name a in symbol table g to the value w (but g is ignored if a is a locative)
693
693
// Result is 0 if error, otherwise low 2 bits are x1 = final assignment, 1x = local assignment, others garbage
694
-
// flags set in jt: bit 0=this is a final assignment;
694
+
// flags set in jt: bit 0=this is a final assignment; bit 1 always 0
695
695
Ijtsymbis(Jjt,Aa,Aw,Ag){F2PREFIP;
696
696
ARGCHK2(a,w);
697
697
Ianmf=NAV(a)->flag; // fetch flags for the name
@@ -739,20 +739,22 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
739
739
if((AR(g)&ARLOCALTABLE)!=0){ // if assignment to a local table (which might not be jt->locsyms)
740
740
I4symx=NAV(a)->symx; // fetch the symbol slot assigned to this name (0 if none)
741
741
e=likely((SGNIF(AR(g),ARLCLONEDX)|(symx-1))>=0)?SYMORIGIN+(I)symx:probeislocal(a,g); // local symbol given and we are using the original table: use the symbol. Otherwise, look up and reserve 1 symbol
742
-
g=0; // indicate we have no lock to clear
742
+
g=(A)((I)jtinplace|-JTASGNWASLOCAL); // indicate local assignment (we have no lock to clear), remember final assignment
743
743
valtype|=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(wt)&QCRAREQD); // enter QCSYMVAL semantics; ra needed if sparse
744
744
}else{ // global table
745
745
SYMRESERVE(1) // before we go into lock, make sure we have a symbol to assign to
746
746
C*bloombase=BLOOMBASE(g); Ichainno=SYMHASH(NAV(a)->hash,AN(g)-SYMLINFOSIZE); // get addr of Bloom filter and the location we are storing to
747
747
valtype|=QCNAMED|QCRAREQD; // must flag local/global type in symbol
748
748
e=probeis(a, g); // get the symbol address to use, old or new. This returns holding a lock on the locale
749
749
// if we are writing to a non-local table, update the table's Bloom filter.
750
+
g=(A)((I)g+((I)jtinplace&JTFINALASGN)); // flags in g: copy final-assignment flag, keep glocal-table flag 0 indicating free needed
750
751
BLOOMSET(bloombase,chainno); // g is under lock. This modifies the shared memory every time - might be better to write only when chain is empty
751
752
// A couple of debugging flags are set during assignment. We don't bother for local names
752
753
if(unlikely(JT(jt,stch)!=0))e->flag|=LCH; // update 'changed' flag if enabled - needed only for globals
753
754
e->sn=jt->currslistx; // Save the script in which this name was defined - meaningful only for globals
754
755
}
755
-
// ****** if g is a global table, we have a write lock on the locale, which we must release in any error paths. g=0 otherwise *******
756
+
// ****** if g is a global table, bit2=0 and we have a write lock on the locale, which we must release in any error paths. The low 2 bits
757
+
// of g are exit flags: bit0=final assignment, bit 1=local assignment. If local assignment, g=-2 (not final) or -1 (final) *******
756
758
757
759
Ax=e->fval; // if x is 0, this name has not been assigned yet; if nonzero, x points to the incumbent value
758
760
// If we are assigning the same data block that's already there, don't bother with changing use counts or anything else (assignment-in-place)
@@ -775,9 +777,11 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
775
777
// Virtual values were realized earlier, and usecounts guaranteed recursive
776
778
// If the value is abandoned inplaceable, we can just zap it and set its usecount to 1
777
779
// SPARSE nouns must never be inplaceable, because their components are not
778
-
if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value
780
+
// obsolete if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value, in local table
781
+
if(((I)g+SGNTO0(AC(w)))==0){ // if final assignment of abandoned value, in local table (g=-1 and inplace bit set)
779
782
// We can zap abandoned nouns. But only if they are final assignment: something like nm:_ [ nm=. 4+4 would free the active block if we zapped, when the FAOWED was applied
780
-
// also, if the name is global it may vanish, along with the value, at any time, so we
783
+
// the idea is that the assignment will protect the value; this is true for local assignment but not for global, because another thread may free the name immediately.
784
+
// If we could be sure that the result of the sentence would never be inspected we could return a value that might be freed, but without such a guarantee we can zap only local assignment.
781
785
AFLAGORLOCAL(w,AFKNOWNNAMED); // indicate the value is in a name. We do this to allow virtual extension.
782
786
// very often a final assignment will assign the abandoned result of the last computation. These values accumulate on the tstack and have to
783
787
// be popped off every now & then in jtxdefn, since there is nothing else to pop them. We detect this important case, in which the zaploc of the
@@ -802,15 +806,16 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
802
806
}
803
807
}elsex=0; // repurpose x to be the value needing fa
804
808
// x here is the value that needs to be freed
805
-
if(g!=0)WRITEUNLOCK(g->lock)elsejtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return
// obsolete else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return
806
811
// ************* we have released the write lock
807
812
// If this is a reassignment, we need to decrement the use count in the old value, since that value is no longer used. Do so after the new value is raised,
808
813
// in case the new value was being protected by the old (ex: n =. >n).
809
814
// It is the responsibility of parse to keep the usecount of a named value raised until it has come out of execution
810
815
SYMVALFA2(x); // if the old value needs to be traversed in detail, do it now outside of lock (subroutine call)
811
-
R (I)jtinplace; // good return, with bit 0 set if final assignment, bit 1 if local
816
+
R (I)g; // good return, with bit 0 set if final assignment, bit 1 if local
Copy file name to clipboardExpand all lines: jsrc/vcat.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -357,7 +357,7 @@ A jtapip(J jt, A a, A w){F2PREFIP;A h;
357
357
Ilgk=bplg(at); Ilgatomsini=MAX(LGSZI-lgk,0); // lg of size of atom of a; lg of number of atoms in an I (0 if <1)
358
358
// Because the test for inplaceability is rather lengthy, start with a quick check of the atom counts. If adding the atoms in w to those in a
359
359
// would push a over a power-of-2 boundary, skip the rest of the testing. We detect this by absence of carry out of the high bit (inside EXTENDINPLACE)
0 commit comments