@@ -250,6 +250,7 @@ setPrologFlag(const char *name, unsigned int flags, ...)
250250 f -> index = 0 ;
251251 f -> flags = flags ;
252252 f -> oneof = NULL ;
253+ f -> pushed = NULL ;
253254 addNewHTableWP (GD -> prolog_flag .table , an , f );
254255 first_def = true;
255256 }
@@ -326,22 +327,32 @@ clean_prolog_flag(prolog_flag *f)
326327 oneof * of ;
327328
328329 set_flag_type (f , FT_INTEGER );
329- switch (type )
330- { case FT_TERM :
331- PL_erase (f -> value .t );
332- break ;
333- case FT_ATOM :
334- PL_unregister_atom (f -> value .a );
335- break ;
336- default :
337- ;
330+ if ( !(f -> flags & FF_PUSH_ABSENT ) )
331+ { switch (type )
332+ { case FT_TERM :
333+ PL_erase (f -> value .t );
334+ break ;
335+ case FT_ATOM :
336+ PL_unregister_atom (f -> value .a );
337+ break ;
338+ default :
339+ ;
340+ }
338341 }
339342 memset (& f -> value , 0 , sizeof (f -> value ));
340343
341344 if ( (of = f -> oneof ) && -- of -> references == 0 )
342345 { f -> oneof = NULL ;
343346 free_oneof (of );
344347 }
348+
349+ while ( f -> pushed )
350+ { prolog_flag * p = f -> pushed ;
351+ f -> pushed = p -> pushed ;
352+ p -> pushed = NULL ;
353+ clean_prolog_flag (p );
354+ freeHeap (p , sizeof (* p ));
355+ }
345356}
346357
347358#ifdef O_PLMT
@@ -350,6 +361,7 @@ copy_prolog_flag(const prolog_flag *f)
350361{ prolog_flag * copy = allocHeapOrHalt (sizeof (* copy ));
351362
352363 * copy = * f ;
364+ copy -> pushed = NULL ;
353365 if ( f -> oneof )
354366 f -> oneof -> references ++ ;
355367 switch ((f -> flags & FT_MASK ))
@@ -1087,6 +1099,7 @@ new_prolog_flag(DECL_LD term_t value, unsigned int flags)
10871099 prolog_flag * f = allocHeapOrHalt (sizeof (* f ));
10881100 f -> index = 0 ;
10891101 f -> oneof = NULL ;
1102+ f -> pushed = NULL ;
10901103
10911104 switch ( (flags & FT_MASK ) )
10921105 { case FT_FROM_VALUE :
@@ -1633,6 +1646,192 @@ PRED_IMPL("create_prolog_flag", 3, create_prolog_flag, PL_FA_ISO)
16331646 return f != NULL ;
16341647}
16351648
1649+
1650+ /*******************************
1651+ * PUSH / POP FLAGS *
1652+ *******************************/
1653+
1654+ /** push_prolog_flag(+Flag, +Value) is det.
1655+
1656+ Save the current thread-local value of Flag (or that it does not exist)
1657+ and set it to Value. Nestable. See pop_prolog_flag/1.
1658+ */
1659+
1660+ /** pop_prolog_flag(+Flag) is det.
1661+
1662+ Restore the last state saved by push_prolog_flag/2. If Flag was absent
1663+ at the matching push, it is removed again. Raises existence_error if
1664+ no matching push exists on the current thread.
1665+ */
1666+
1667+ #define put_saved_flag_value (val , saved ) \
1668+ LDFUNC(put_saved_flag_value, val, saved)
1669+
1670+ static bool
1671+ put_saved_flag_value (DECL_LD term_t val , const prolog_flag * saved )
1672+ { switch (saved -> flags & FT_MASK )
1673+ { case FT_BOOL :
1674+ case FT_ATOM :
1675+ return PL_put_atom (val , saved -> value .a );
1676+ case FT_INTEGER :
1677+ return PL_put_int64 (val , saved -> value .i );
1678+ case FT_FLOAT :
1679+ return PL_put_float (val , saved -> value .f );
1680+ case FT_TERM :
1681+ return PL_recorded (saved -> value .t , val );
1682+ }
1683+ assert (0 );
1684+ return false;
1685+ }
1686+
1687+ #define ensure_local_flag (k , f ) \
1688+ LDFUNC(ensure_local_flag, k, f)
1689+
1690+ /* Return a thread-local prolog_flag for `k`. Unlike the copy-on-write
1691+ set_prolog_flag path, we force a local copy even when only one thread
1692+ exists: otherwise a second thread created *between* push and pop
1693+ would cause an internal set_prolog_flag to COW the flag into LD,
1694+ leaving the pushed chain stranded in the GD entry and making pop
1695+ fail to find it.
1696+ */
1697+ static prolog_flag *
1698+ ensure_local_flag (DECL_LD atom_t k , prolog_flag * f )
1699+ {
1700+ #ifdef O_PLMT
1701+ if ( !(f -> flags & FF_ISLOCAL ) )
1702+ { f = copy_prolog_flag (f );
1703+ register_local_flag (k , f );
1704+ PL_register_atom (k );
1705+ }
1706+ #endif
1707+ return f ;
1708+ }
1709+
1710+ static
1711+ PRED_IMPL ("push_prolog_flag" , 2 , push_prolog_flag , 0 )
1712+ { PRED_LD
1713+ atom_t k ;
1714+ Module m = MODULE_parse ;
1715+ term_t key = PL_new_term_ref ();
1716+ term_t value = A2 ;
1717+
1718+ if ( !PL_strip_module (A1 , & m , key ) ||
1719+ !PL_get_atom_ex (key , & k ) )
1720+ return false;
1721+
1722+ PL_LOCK (L_PLFLAG );
1723+ prolog_flag * f = NULL ;
1724+ #ifdef O_PLMT
1725+ if ( LD -> prolog_flag .table )
1726+ f = lookupHTableWP (LD -> prolog_flag .table , k );
1727+ #endif
1728+ if ( !f )
1729+ f = lookupHTableWP (GD -> prolog_flag .table , k );
1730+
1731+ bool rc ;
1732+ if ( f )
1733+ { if ( !check_flag_write_access (f , k , 0 ) )
1734+ { PL_UNLOCK (L_PLFLAG );
1735+ return false;
1736+ }
1737+ f = ensure_local_flag (k , f );
1738+ prolog_flag * saved = copy_prolog_flag (f );
1739+ rc = set_flag_value (f , m , k , value );
1740+ if ( rc )
1741+ { saved -> pushed = f -> pushed ;
1742+ f -> pushed = saved ;
1743+ } else
1744+ { clean_prolog_flag (saved );
1745+ freeHeap (saved , sizeof (* saved ));
1746+ }
1747+ } else
1748+ { prolog_flag * nf = new_prolog_flag (value , FT_FROM_VALUE );
1749+ if ( nf )
1750+ { prolog_flag * absent = allocHeapOrHalt (sizeof (* absent ));
1751+ memset (absent , 0 , sizeof (* absent ));
1752+ absent -> flags = FT_INTEGER |FF_PUSH_ABSENT ;
1753+ nf -> pushed = absent ;
1754+ #ifdef O_PLMT
1755+ register_local_flag (k , nf );
1756+ PL_register_atom (k );
1757+ #else
1758+ addNewHTableWP (GD -> prolog_flag .table , k , nf );
1759+ PL_register_atom (k );
1760+ #endif
1761+ rc = true;
1762+ } else
1763+ { rc = false;
1764+ }
1765+ }
1766+ PL_UNLOCK (L_PLFLAG );
1767+
1768+ return rc ;
1769+ }
1770+
1771+ static
1772+ PRED_IMPL ("pop_prolog_flag" , 1 , pop_prolog_flag , 0 )
1773+ { PRED_LD
1774+ atom_t k ;
1775+ Module m = MODULE_parse ;
1776+ term_t key = PL_new_term_ref ();
1777+
1778+ if ( !PL_strip_module (A1 , & m , key ) ||
1779+ !PL_get_atom_ex (key , & k ) )
1780+ return false;
1781+
1782+ PL_LOCK (L_PLFLAG );
1783+ prolog_flag * f = NULL ;
1784+ bool in_local = false;
1785+ #ifdef O_PLMT
1786+ if ( LD -> prolog_flag .table )
1787+ { f = lookupHTableWP (LD -> prolog_flag .table , k );
1788+ in_local = (f != NULL );
1789+ }
1790+ #endif
1791+ if ( !f )
1792+ f = lookupHTableWP (GD -> prolog_flag .table , k );
1793+
1794+ if ( !f || !f -> pushed )
1795+ { PL_UNLOCK (L_PLFLAG );
1796+ term_t ex = PL_new_term_ref ();
1797+ return ( PL_put_atom (ex , k ) &&
1798+ PL_error (NULL , 0 , NULL , ERR_EXISTENCE ,
1799+ ATOM_pushed_flag , ex ) );
1800+ }
1801+
1802+ prolog_flag * prev = f -> pushed ;
1803+ bool rc ;
1804+
1805+ if ( prev -> flags & FF_PUSH_ABSENT )
1806+ { f -> pushed = NULL ;
1807+ #ifdef O_PLMT
1808+ if ( in_local )
1809+ deleteHTableWP (LD -> prolog_flag .table , k );
1810+ else
1811+ #endif
1812+ deleteHTableWP (GD -> prolog_flag .table , k );
1813+ clean_prolog_flag (f );
1814+ freeHeap (f , sizeof (* f ));
1815+ PL_unregister_atom (k );
1816+ freeHeap (prev , sizeof (* prev ));
1817+ rc = true;
1818+ } else
1819+ { term_t restore = PL_new_term_ref ();
1820+ rc = put_saved_flag_value (restore , prev );
1821+ if ( rc )
1822+ { f -> pushed = prev -> pushed ;
1823+ prev -> pushed = NULL ;
1824+ rc = set_flag_value (f , m , k , restore );
1825+ clean_prolog_flag (prev );
1826+ freeHeap (prev , sizeof (* prev ));
1827+ }
1828+ }
1829+ PL_UNLOCK (L_PLFLAG );
1830+
1831+ return rc ;
1832+ }
1833+
1834+
16361835bool
16371836PL_current_prolog_flag (atom_t name , int type , void * value )
16381837{ prolog_flag * f ;
@@ -2598,4 +2797,6 @@ BeginPredDefs(prologflag)
25982797 PRED_DEF ("create_prolog_flag" , 3 , create_prolog_flag , 0 )
25992798 PRED_DEF ("current_prolog_flag" , 2 , current_prolog_flag , PL_FA_ISO |NDET )
26002799 PRED_DEF ("$current_prolog_flag" , 5 , dcurrent_prolog_flag , NDET )
2800+ PRED_DEF ("push_prolog_flag" , 2 , push_prolog_flag , 0 )
2801+ PRED_DEF ("pop_prolog_flag" , 1 , pop_prolog_flag , 0 )
26012802EndPredDefs
0 commit comments