bbgc_mx.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. #ifdef BB_THREADS
  2. #include "bbmonkey.h"
  3. //#include "bbgc_mx.h"
  4. #include "bbweakref.h"
  5. #include <mutex>
  6. #include <condition_variable>
  7. #ifdef _WIN32
  8. #include <windows.h>
  9. #else
  10. #include <pthread.h>
  11. #include <unistd.h>
  12. #include <signal.h>
  13. #endif
  14. namespace bbDB{
  15. void stop();
  16. void stopped();
  17. void error( bbString err );
  18. }
  19. namespace{
  20. typedef std::chrono::duration<double> Duration;
  21. typedef std::chrono::high_resolution_clock Clock;
  22. double now(){
  23. static Clock::time_point start=Clock::now();
  24. auto elapsed=(Clock::now()-start).count();
  25. return elapsed * ((double)Clock::period::num/(double)Clock::period::den);
  26. }
  27. }
  28. namespace bbGC{
  29. const char *state="IDLE";
  30. size_t trigger=4*1024*1024;
  31. int suspended=1;
  32. size_t memused;
  33. size_t malloced;
  34. bbGCThread *threads;
  35. thread_local bbGCThread *currentThread;
  36. thread_local bbGCFiber *currentFiber;
  37. std::atomic_char markedBit;
  38. std::atomic_char unmarkedBit;
  39. std::atomic<bbGCNode*> markQueue;
  40. std::mutex collectorMutex;
  41. bbGCRoot *roots;
  42. bbGCTmp *retained;
  43. bbGCNode *markedList;
  44. bbGCNode *unmarkedList;
  45. bbGCNode markLists[2];
  46. bbGCNode freeList;
  47. size_t markedBytes;
  48. size_t unmarkedBytes;
  49. size_t allocedBytes;
  50. void *pools[32];
  51. unsigned char *poolBuf;
  52. size_t poolBufSize;
  53. std::mutex poolsMutex;
  54. void suspendSigHandler( int sig );
  55. bool inited;
  56. void finit(){
  57. suspended=0x7fffffff;
  58. inited=false;
  59. }
  60. void init(){
  61. if( inited ) return;
  62. inited=true;
  63. // bb_printf( "Initializing threaded GC\n" );
  64. markedBit=1;
  65. markedList=&markLists[0];
  66. markedList->succ=markedList->pred=markedList;
  67. unmarkedBit=2;
  68. unmarkedList=&markLists[1];
  69. unmarkedList->succ=unmarkedList->pred=unmarkedList;
  70. freeList.succ=freeList.pred=&freeList;
  71. threads=new bbGCThread;
  72. currentThread=threads;
  73. currentFiber=currentThread->fibers;
  74. #ifndef _WIN32
  75. struct sigaction action;
  76. memset( &action,0,sizeof(action) );
  77. action.sa_handler=suspendSigHandler;
  78. action.sa_flags=SA_RESTART;
  79. if( sigaction( SIGUSR2,&action,0 )<0 ) exit(-1);
  80. #endif
  81. suspended=0;
  82. atexit( finit );
  83. }
  84. void setTrigger( size_t size ){
  85. trigger=size;
  86. }
  87. void suspend(){
  88. ++suspended;
  89. }
  90. void resume(){
  91. --suspended;
  92. }
  93. __forceinline void insert( bbGCNode *p,bbGCNode *succ ){
  94. p->succ=succ;
  95. p->pred=succ->pred;
  96. p->pred->succ=p;
  97. succ->pred=p;
  98. }
  99. __forceinline void remove( bbGCNode *p ){
  100. p->pred->succ=p->succ;
  101. p->succ->pred=p->pred;
  102. }
  103. void lockCollector(){
  104. // printf( "lockCollector\n" );fflush( stdout );
  105. if( inited ) collectorMutex.lock();
  106. }
  107. void unlockCollector(){
  108. // printf( "unlockCollector\n" );fflush( stdout );
  109. if( inited ) collectorMutex.unlock();
  110. }
  111. #ifdef _WIN32
  112. //collectorMutex locked.
  113. //
  114. void suspendThreads(){
  115. for( bbGCThread *thread=currentThread->succ;thread!=currentThread;thread=thread->succ ){
  116. int n=(int)SuspendThread( thread->handle );
  117. if( n<0 ){ printf( "SuspendThread failed! n=%i\n",n );fflush( stdout );exit( -1 ); }
  118. }
  119. for( bbGCThread *thread=currentThread->succ;thread!=currentThread;thread=thread->succ ){
  120. CONTEXT context={0};//CONTEXT_CONTROL};
  121. if( !GetThreadContext( thread->handle,&context ) ){ printf( "GetThreadContext failed\n" );fflush( stdout );exit( -1 ); }
  122. }
  123. }
  124. //collectorMutex locked.
  125. //
  126. void resumeThreads(){
  127. for( bbGCThread *thread=currentThread->succ;thread!=currentThread;thread=thread->succ ){
  128. ResumeThread( thread->handle );
  129. }
  130. }
  131. #else
  132. std::mutex suspendMutex;
  133. std::condition_variable_any suspendCondvar;
  134. std::atomic_int threadsSuspending{0};
  135. std::mutex resumeMutex;
  136. std::condition_variable_any resumeCondvar;
  137. std::atomic_int resumeCount{0};
  138. void suspendSigHandler( int sig ){
  139. int resume=resumeCount+1;
  140. //signal suspended
  141. suspendMutex.lock();
  142. threadsSuspending-=1;
  143. suspendMutex.unlock();
  144. suspendCondvar.notify_one();
  145. //wait for resume
  146. resumeMutex.lock();
  147. while( resumeCount!=resume ) resumeCondvar.wait( resumeMutex );
  148. resumeMutex.unlock();
  149. }
  150. //collectorMutex locked.
  151. //
  152. void suspendThreads(){
  153. threadsSuspending=0;
  154. for( bbGCThread *thread=currentThread->succ;thread!=currentThread;thread=thread->succ ){
  155. ++threadsSuspending;
  156. }
  157. for( bbGCThread *thread=currentThread->succ;thread!=currentThread;thread=thread->succ ){
  158. pthread_kill( (pthread_t)thread->handle,SIGUSR2 );
  159. }
  160. suspendMutex.lock();
  161. while( threadsSuspending ) suspendCondvar.wait( suspendMutex );
  162. suspendMutex.unlock();
  163. }
  164. //collectorMutex locked.
  165. //
  166. void resumeThreads(){
  167. //signal resume
  168. resumeMutex.lock();
  169. resumeCount+=1;
  170. resumeMutex.unlock();
  171. resumeCondvar.notify_all();
  172. }
  173. #endif
  174. //collectorMutex locked.
  175. //
  176. void reclaim( size_t size ){
  177. size_t freed=0;
  178. while( freeList.succ!=&freeList && freed<size ){
  179. bbGCNode *p=freeList.succ;
  180. freed+=mallocSize( p );
  181. remove( p );
  182. if( p->flags & 2 ){
  183. //printf( "deleting weak refs for: %s %p\n",p->typeName(),p );fflush( stdout );
  184. bbGCWeakRef **pred=&bbGC::weakRefs,*curr;
  185. while( curr=*pred ){
  186. if( curr->target==p ){
  187. curr->target=0;
  188. *pred=curr->succ;
  189. }else{
  190. pred=&curr->succ;
  191. }
  192. }
  193. }
  194. if( p->flags & 1 ){
  195. //printf( "finalizing: %s %p\n",p->typeName(),p );fflush( stdout );
  196. ++suspended;
  197. p->state=char(unmarkedBit);
  198. p->gcFinalize();
  199. if( p->state==markedBit ) bbRuntimeError( "Object resurrected in finalizer" );
  200. --suspended;
  201. }
  202. p->~bbGCNode();
  203. bbGC::free( p );
  204. }
  205. }
  206. //collectorMutex locked.
  207. //
  208. void markRoots(){
  209. for( bbGCRoot *root=roots;root;root=root->succ ){
  210. root->gcMark();
  211. }
  212. }
  213. //collectorMutex locked + threads suspended.
  214. //
  215. void markRetained(){
  216. for( bbGCTmp *tmp=retained;tmp;tmp=tmp->succ ){
  217. enqueue( tmp->node );
  218. }
  219. }
  220. //collectorMutex locked + threads suspended.
  221. //
  222. void markFibers(){
  223. bbGCThread *thread=threads;
  224. for(;;){
  225. bbGCMark( thread->entry );
  226. bbGCFiber *fiber=thread->fibers;
  227. for(;;){
  228. bbGCMark( fiber->entry );
  229. for( bbGCFrame *frame=fiber->frames;frame;frame=frame->succ ){
  230. frame->gcMark();
  231. }
  232. for( bbGCNode *node=fiber->ctoring;node;node=node->qsucc ){
  233. node->gcMark();
  234. }
  235. for( bbGCTmp *tmp=fiber->tmps;tmp;tmp=tmp->succ ){
  236. enqueue( tmp->node );
  237. }
  238. fiber=fiber->succ;
  239. if( fiber==thread->fibers ) break;
  240. }
  241. thread=thread->succ;
  242. if( thread==threads ) break;
  243. }
  244. }
  245. //collectorMutex locked.
  246. //
  247. void markQueued( size_t tomark ){
  248. while( markQueue && markedBytes<tomark ){
  249. bbGCNode *p=markQueue;
  250. while( !markQueue.compare_exchange_weak( p,p->qsucc ) ){}
  251. remove( p );
  252. p->gcMark();
  253. insert( p,markedList );
  254. p->state=char(markedBit);
  255. markedBytes+=mallocSize( p );
  256. }
  257. }
  258. //collectorMutex locked.
  259. //
  260. void sweep(){
  261. double start=now();
  262. // printf( "GC info: sweeping...\n" );fflush( stdout );
  263. suspendThreads();
  264. markRetained();
  265. markFibers();
  266. markQueued( SIZE_MAX );
  267. if( unmarkedList->succ!=unmarkedList ){
  268. //append unmarked to end of free queue
  269. unmarkedList->succ->pred=freeList.pred;
  270. unmarkedList->pred->succ=&freeList;
  271. freeList.pred->succ=unmarkedList->succ;
  272. freeList.pred=unmarkedList->pred;
  273. //clear unmarked
  274. unmarkedList->succ=unmarkedList->pred=unmarkedList;
  275. }
  276. //swap mark/unmarked lists
  277. auto tmp1=markedList;markedList=unmarkedList;unmarkedList=tmp1;
  278. auto tmp2=char(markedBit);markedBit=char(unmarkedBit);unmarkedBit=tmp2;
  279. unmarkedBytes=markedBytes;
  280. markedBytes=0;
  281. //start new sweep phase
  282. allocedBytes=0;
  283. resumeThreads();
  284. markRoots();
  285. double elapsed=now()-start;
  286. // bb_printf( "sweep=%g (%ims)\n",elapsed,int(elapsed*1000+0.5) );fflush( stdout );
  287. // printf( "end sweep\n" );fflush( stdout );
  288. }
  289. void retain( bbGCNode *node ){
  290. if( !node ) return;
  291. bbGCTmp *tmp=currentFiber->freeTmps;
  292. if( tmp ) currentFiber->freeTmps=tmp->succ; else tmp=new bbGCTmp;
  293. tmp->node=node;
  294. lockCollector();
  295. tmp->succ=retained;
  296. retained=tmp;
  297. unlockCollector();
  298. }
  299. void release( bbGCNode *node ){
  300. if( !node ) return;
  301. lockCollector();
  302. bbGCTmp **p=&retained;
  303. while( bbGCTmp *tmp=*p ){
  304. if( tmp->node!=node ){
  305. p=&tmp->succ;
  306. continue;
  307. }
  308. *p=tmp->succ;
  309. tmp->succ=currentFiber->freeTmps;
  310. currentFiber->freeTmps=tmp;
  311. break;
  312. }
  313. unlockCollector();
  314. }
  315. void *malloc( size_t size ){
  316. // printf( "malloc %u\n",size );fflush( stdout );
  317. size=(size+8+7) & ~7;
  318. memused+=size;
  319. if( !suspended ){
  320. lockCollector();
  321. if( allocedBytes+size>=trigger ){
  322. state="SWEEPING";
  323. sweep();
  324. }else{
  325. state="MARKING";
  326. markQueued( double( allocedBytes+size ) / double( trigger ) * double( unmarkedBytes + trigger ) );
  327. }
  328. state="RECLAIMING";
  329. reclaim( size );
  330. state="IDLE";
  331. unlockCollector();
  332. }
  333. void *p;
  334. if( size<256 ){
  335. if( inited ) poolsMutex.lock();
  336. if( pools[size>>3] ){
  337. p=pools[size>>3];
  338. pools[size>>3]=*(void**)p;
  339. }else{
  340. if( size>poolBufSize ){
  341. if( poolBufSize ){
  342. *(void**)poolBuf=pools[poolBufSize>>3];
  343. pools[poolBufSize>>3]=poolBuf;
  344. }
  345. poolBufSize=65536;
  346. poolBuf=(unsigned char*)::malloc( poolBufSize );
  347. malloced+=poolBufSize;
  348. }
  349. p=poolBuf;
  350. poolBuf+=size;
  351. poolBufSize-=size;
  352. }
  353. if( inited ) poolsMutex.unlock();
  354. }else{
  355. p=::malloc( size );
  356. malloced+=size;
  357. }
  358. allocedBytes+=size;
  359. size_t *q=(size_t*)p;
  360. if( sizeof(size_t)==4 ) ++q;
  361. *q++=size;
  362. // printf( "end malloc %u\n",size );fflush( stdout );
  363. return q;
  364. }
  365. size_t mallocSize( void *p ){
  366. if( p ) return *((size_t*)p-1);
  367. return 0;
  368. }
  369. void free( void *p ){
  370. if( !p ) return;
  371. size_t *q=(size_t*)p;
  372. size_t size=*--q;
  373. if( sizeof(size_t)==4 ) --q;
  374. #ifndef NDEBUG
  375. memset( q,0xa5,size );
  376. #endif
  377. memused-=size;
  378. if( size<256 ){
  379. if( inited ) poolsMutex.lock();
  380. *(void**)q=pools[size>>3];
  381. pools[size>>3]=q;
  382. if( inited ) poolsMutex.unlock();
  383. }else{
  384. malloced-=size;
  385. ::free( q );
  386. }
  387. }
  388. void collect(){
  389. if( !inited ) return;
  390. static size_t maxused;
  391. lockCollector();
  392. sweep();
  393. reclaim( SIZE_MAX );
  394. unlockCollector();
  395. if( memused>maxused ) maxused=memused;
  396. // printf( "Collect complete: memused=%i max memused=%i\n",memused,maxused );fflush( stdout );
  397. }
  398. #ifndef NDEBUG
  399. void qinsert( bbGCNode *p ){
  400. static int max_n;
  401. int n=0;
  402. p->qsucc=markQueue;
  403. while( !markQueue.compare_exchange_weak( p->qsucc,p ) ){ ++n; }
  404. if( n>max_n ){ max_n=n;printf( "GC info: max spins=%i\n",max_n );fflush( stdout ); }
  405. }
  406. void enqueue( bbGCNode *p ){
  407. if( !p || p->state.load()!=unmarkedBit ) return;
  408. char oldstate=p->state.exchange( 4 );
  409. if( oldstate==4 ) return;
  410. if( oldstate!=unmarkedBit ){ printf( "GC info: redundant enqueue\n" );fflush( stdout ); }
  411. qinsert( p );
  412. }
  413. void beginCtor( bbGCNode *p ){
  414. p->state=4;
  415. p->flags=0;
  416. p->qsucc=currentFiber->ctoring;
  417. currentFiber->ctoring=p;
  418. }
  419. void endCtor( bbGCNode *p ){
  420. currentFiber->ctoring=p->qsucc;
  421. p->succ=p->pred=p;
  422. qinsert( p );
  423. }
  424. #endif
  425. }
  426. // ***** bbGCNode *****
  427. void bbGCNode::gcNeedsFinalize(){
  428. flags|=1;
  429. }
  430. void bbGCNode::gcFinalize(){
  431. }
  432. void bbGCNode::gcMark(){
  433. }
  434. void bbGCNode::dbEmit(){
  435. }
  436. const char *bbGCNode::typeName()const{
  437. return "bbGCNode";
  438. }
  439. // ***** bbGCThread *****
  440. bbGCThread::bbGCThread():succ( this ),pred( this ),fibers( new bbGCFiber ){
  441. #if _WIN32
  442. handle=OpenThread( THREAD_ALL_ACCESS,FALSE,GetCurrentThreadId() );
  443. #else
  444. handle=(void*)pthread_self();
  445. #endif
  446. }
  447. bbGCThread::~bbGCThread(){
  448. #ifdef _WIN32
  449. CloseHandle( handle );
  450. #endif
  451. }
  452. void bbGCThread::link(){
  453. bbGC::lockCollector();
  454. succ=bbGC::threads;
  455. pred=bbGC::threads->pred;
  456. bbGC::threads->pred=this;
  457. pred->succ=this;
  458. bbGC::currentThread=this;
  459. bbGC::currentFiber=fibers;
  460. bbGC::unlockCollector();
  461. }
  462. void bbGCThread::unlink(){
  463. bbGC::lockCollector();
  464. pred->succ=succ;
  465. succ->pred=pred;
  466. bbGC::currentThread=0;
  467. bbGC::currentFiber=0;
  468. bbGC::unlockCollector();
  469. }
  470. // ***** bbGCFiber *****
  471. bbGCFiber::bbGCFiber():succ( this ),pred( this ),frames( nullptr ),tmps( nullptr ),freeTmps( nullptr ),ctoring( nullptr ){
  472. }
  473. void bbGCFiber::link(){
  474. succ=bbGC::currentThread->fibers;
  475. pred=bbGC::currentThread->fibers->pred;
  476. bbGC::currentThread->fibers->pred=this;
  477. pred->succ=this;
  478. }
  479. void bbGCFiber::unlink(){
  480. pred->succ=succ;
  481. succ->pred=pred;
  482. }
  483. // ***** bbGCFrame *****
  484. void bbGCFrame::gcMark(){
  485. }
  486. // ***** bbGCRoot *****
  487. bbGCRoot::bbGCRoot(){
  488. bbGC::lockCollector();
  489. succ=bbGC::roots;
  490. bbGC::roots=this;
  491. bbGC::unlockCollector();
  492. }
  493. void bbGCRoot::gcMark(){
  494. }
  495. #endif