source: Include/system/gc.sbp@ 202

Last change on this file since 202 was 202, checked in by イグトランス (egtra), 17 years ago

sweep処理中にsweepが呼ばれてしまうことがあるらしく、デッドロックが起こることがあったのをisSweepingの活用によって修正。ついでにダブルチェックドロック化。

File size: 11.4 KB
RevLine 
[109]1/*
2 このファイルでは、ABのガベージコレクションの実装を行います。
3*/
[1]4
[109]5
[1]6/*
7※これらの変数はコンパイラが自動的に定義します。
8Dim _System_gc_GlobalRoot_StartPtr As VoidPtr
9Dim _System_gc_GlobalRoot_Size As Long
10Dim _System_gc_StackRoot_StartPtr As VoidPtr
11*/
12
13Function _System_GetSp() As LONG_PTR 'dummy
14End Function
15
16
17Const _System_GC_FLAG_ATOMIC = 1
18Const _System_GC_FLAG_NEEDFREE = 2
19Const _System_GC_FLAG_INITZERO = 4
[144]20Const _System_GC_FLAG_OBJECT = 8
[1]21
22Class _System_CGarbageCollection
[144]23 ppPtr As *VoidPtr
[1]24 pSize As *SIZE_T
[144]25 pdwFlags As *DWord
[1]26 n As Long
27
28 iAllSize As SIZE_T
29
[144]30 isSweeping As Boolean
31
[1]32 CriticalSection As CRITICAL_SECTION
33
[109]34 'メモリの上限値(この値を超えるとGCが発動します)
35 '※バイト単位
[144]36 limitMemorySize As LONG_PTR
[109]37
[144]38 isFinish As Boolean
39
[1]40Public
[144]41
42 ' 特殊クラスのため、コンストラクタ・デストラクタは呼ばれません
[1]43 Sub _System_CGarbageCollection()
[144]44 End Sub
45 Sub ~_System_CGarbageCollection()
46 End Sub
47
48 Sub Begin()
[1]49 If ppPtr Then Exit Sub
50
[144]51 isFinish = False
52
53 'メモリの上限値(この値を超えるとGCが発動します)
54 '※バイト単位
55 limitMemorySize = 1024*1024 As LONG_PTR
56
57 ppPtr=_System_calloc( 1 )
58 pSize=_System_calloc( 1 )
59 pdwFlags=_System_calloc( 1 )
[1]60 n=0
61
62 iAllSize=0
63
[144]64 ' スウィープ中かどうか
65 isSweeping = False
[1]66
67 'クリティカルセッションを生成
68 InitializeCriticalSection(CriticalSection)
69
[18]70
71 '---------------------------
72 ' 開始時のスレッドを通知
73 '---------------------------
[1]74 Dim hTargetThread As HANDLE
75 DuplicateHandle(GetCurrentProcess(),
76 GetCurrentThread(),
77 GetCurrentProcess(),
78 hTargetThread, 0, FALSE, DUPLICATE_SAME_ACCESS) 'カレントスレッドのハンドルを複製
[18]79
[144]80 ' スレッド管理用オブジェクトを生成
81 _System_pobj_AllThreads = New _System_CThreadCollection()
[18]82
[144]83 ' 自身のThreadオブジェクトを生成
84 Dim thread As Thread(hTargetThread,GetCurrentThreadId(),0)
[18]85
[144]86 _System_pobj_AllThreads->BeginThread(VarPtr( thread ),_System_gc_StackRoot_StartPtr As *LONG_PTR)
87
[1]88 End Sub
[144]89 Sub Finish()
[1]90 If ppPtr=0 Then Exit Sub
91
[144]92 isFinish = True
[79]93
[144]94 ' スレッド管理用オブジェクトを破棄
95 Delete _System_pobj_AllThreads
[1]96
[144]97 ' 自分以外のスレッドを一時停止
98 '_System_pobj_AllThreads->SuspendAnotherThread()
99
100 OutputDebugString( Ex"garbage colletion sweeping all memory objects!\r\n" )
101 DeleteAllGarbageMemories()
102
103 ' 未解放のメモリオブジェクトをトレース
104 DumpMemoryLeaks()
105
106 ' 自分以外のスレッドを再開
107 '_System_pobj_AllThreads->ResumeAnotherThread()
108
[1]109 HeapFree(_System_hProcessHeap,0,ppPtr)
110 ppPtr=0
111
112 HeapFree(_System_hProcessHeap,0,pSize)
113 pSize=0
[144]114 HeapFree(_System_hProcessHeap,0,pdwFlags)
115 pdwFlags=0
[1]116
117 'クリティカルセッションを破棄
118 DeleteCriticalSection(CriticalSection)
[144]119
[79]120 End Sub
[1]121
[170]122 Sub add(new_ptr As VoidPtr, size As SIZE_T, flags As DWord)
[1]123 iAllSize+=size
124
[144]125 EnterCriticalSection(CriticalSection)
126 ppPtr=HeapReAlloc(_System_hProcessHeap,0,ppPtr,(n+1)*SizeOf(VoidPtr))
127 ppPtr[n]=new_ptr
[1]128
[144]129 pSize=HeapReAlloc(_System_hProcessHeap,0,pSize,(n+1)*SizeOf(SIZE_T))
130 pSize[n]=size
[1]131
[144]132 pdwFlags=HeapReAlloc(_System_hProcessHeap,0,pdwFlags,(n+1)*SizeOf(DWord))
133 pdwFlags[n]=flags
134 LeaveCriticalSection(CriticalSection)
[1]135
136 n++
137 End Sub
138
139
140 Function __malloc(size As SIZE_T,flags As Byte) As VoidPtr
141 EnterCriticalSection(CriticalSection)
142 Dim dwFlags As DWord
143 If flags and _System_GC_FLAG_INITZERO Then
144 dwFlags=HEAP_ZERO_MEMORY
145 Else
146 dwFlags=0
147 End If
148
[144]149 Dim ptr = HeapAlloc(_System_hProcessHeap,dwFlags,size)
150 add( ptr, size, flags )
151 LeaveCriticalSection(CriticalSection)
[1]152
[144]153 Return ptr
[1]154 End Function
155
156 Function __realloc(lpMem As VoidPtr, size As SIZE_T) As VoidPtr
[18]157 EnterCriticalSection(CriticalSection)
158 Dim i As Long
159 For i=0 To ELM(n)
160 If ppPtr[i]=lpMem Then
161 iAllSize+=size-pSize[i]
[1]162
[18]163 pSize[i]=size
164 ppPtr[i]=HeapReAlloc(_System_hProcessHeap,HEAP_ZERO_MEMORY,lpMem,size)
165
166 LeaveCriticalSection(CriticalSection)
167 Return ppPtr[i]
168 End If
169 Next
170 LeaveCriticalSection(CriticalSection)
[1]171 Return 0
172 End Function
173
[144]174 Sub __free_ex(lpMem As VoidPtr, isSweeping As Boolean)
[18]175 EnterCriticalSection(CriticalSection)
176 Dim i As Long
177 For i=0 To ELM(n)
178 If ppPtr[i]=lpMem Then
[144]179 If (pdwFlags[i] and _System_GC_FLAG_NEEDFREE)<>0 or isSweeping Then
[18]180 iAllSize-=pSize[i]
[1]181
[18]182 HeapFree(_System_hProcessHeap,0,ppPtr[i])
183 ppPtr[i]=0
184 pSize[i]=0
185 Else
[144]186 If isFinish = False Then
187 OutputDebugString( Ex"heap free missing!\r\n" )
188 End If
[18]189 End If
[1]190 End If
[18]191 Next
192 LeaveCriticalSection(CriticalSection)
[1]193 End Sub
194
[144]195 Sub __free(lpMem As VoidPtr)
196 __free_ex( lpMem, False )
197 End Sub
198
[1]199 Sub sweep()
[202]200 If isSweeping <> False Or iAllSize<limitMemorySize Then
[171]201 'メモリ使用量が上限値を超えていないとき
202 Exit Sub
203 End If
[202]204 isSweeping = True
[171]205 OutputDebugString( Ex"garbage colletion sweep start!\r\n" )
[144]206
[171]207 Dim hThread As HANDLE
208 Dim ThreadId As DWord
209 hThread=_beginthreadex(NULL,0,AddressOf(SweepOnOtherThread),VarPtr(This),0,ThreadId)
210 WaitForSingleObject(hThread,INFINITE)
211 CloseHandle(hThread)
[202]212 isSweeping = False
[1]213 End Sub
214
[144]215Private
[1]216
[144]217 ' 生存検知
218 Function HitTest(pSample As VoidPtr) As Long
219 Dim i As Long
220 For i=0 To ELM(n)
221 If (ppPtr[i] As LONG_PTR)<=(pSample As LONG_PTR) and (pSample As LONG_PTR)<((ppPtr[i] As LONG_PTR)+pSize[i]) Then
222 Return i
223 End If
224 Next
225 Return -1
226 End Function
[1]227
[144]228 ' 指定領域のスキャン
229 Sub Scan(pStartPtr As *LONG_PTR, size As LONG_PTR, pbMark As *Byte)
230 Dim i As Long, count As Long, index As Long
231 count=(size\SizeOf(LONG_PTR)) As Long
232 For i=0 To ELM(count)
233 index=HitTest(pStartPtr[i] As VoidPtr)
234 If index<>-1 Then
235 If pbMark[index]=0 Then
236 pbMark[index]=1
[170]237
[144]238 If (pdwFlags[index] and _System_GC_FLAG_ATOMIC)=0 Then
239 'ヒープ領域がポインタ値を含む可能性があるとき
240 If ppPtr[index] = 0 Then
241 'エラー
[170]242
[144]243 End If
244 Scan(ppPtr[index] As *LONG_PTR,pSize[index],pbMark)
245 End If
246 End If
247 End If
248 Next
249 End Sub
[1]250
[144]251 ' ローカル領域をルートに指定してスキャン
252 Sub LocalScan( pbMark As *Byte )
[18]253 Dim Context As CONTEXT
[1]254 Dim NowSp As *LONG_PTR
255 Dim size As LONG_PTR
[18]256 Dim i As Long
257 For i=0 To ELM(_System_pobj_AllThreads->ThreadNum)
258 If _System_pobj_AllThreads->ppobj_Thread[i] Then
[1]259 FillMemory(VarPtr(Context),SizeOf(CONTEXT),0)
260 Context.ContextFlags=CONTEXT_CONTROL
[18]261 If _System_pobj_AllThreads->ppobj_Thread[i]->__GetContext(Context)=0 Then
[1]262 OutputDebugString(Ex"レジスタ情報の取得に失敗しました。\r\n")
263 End If
264
265#ifdef _WIN64
266 NowSp=Context.Rsp As *LONG_PTR
267#else
268 NowSp=Context.Esp As *LONG_PTR
269#endif
270
[18]271 size=(_System_pobj_AllThreads->pStackBase[i] As LONG_PTR)-(NowSp As LONG_PTR)
[1]272
[144]273 If NowSp = 0 Then
274 debug
275 Exit Sub
276 End If
277
278 Scan( NowSp, size, pbMark )
[1]279 End If
280 Next
[144]281 End Sub
[1]282
[144]283 Sub DeleteGarbageMemories( pbMark As *Byte )
[1]284
[144]285 Dim isAllDelete = False
286 If pbMark = NULL Then
287 ' すべてを破棄するとき
288 isAllDelete = True
289 pbMark = _System_calloc( n )
290 End If
[1]291
[144]292 Dim i As Long
[1]293 For i=0 To ELM(n)
[144]294 If pbMark[i]=0 and ppPtr[i]<>0 and (pdwFlags[i] and _System_GC_FLAG_NEEDFREE)=0 Then
295 If ppPtr[i] = 0 Then
296 If isAllDelete Then
297 Continue
298 Else
299 debug
300 End If
301 End If
[1]302
[144]303 Dim ptr = ppPtr[i]
304 Dim size = pSize[i]
305
[1]306 ppPtr[i]=0
307 pSize[i]=0
[144]308
309 If (pdwFlags[i] and _System_GC_FLAG_OBJECT) <> 0 Then
310 /* ・オブジェクトの個数
311 ・オブジェクトのサイズ
312 ・デストラクタの関数ポインタ
313 を考慮 */
314 _System_SweepingDelete (ptr + SizeOf( LONG_PTR ) * 3 )
315 Else
316 iAllSize-=size
317 HeapFree(_System_hProcessHeap,0,ptr)
318 End If
[1]319 End If
320 Next
321
[144]322 If isAllDelete Then
323 _System_free( pbMark )
324 End If
325
326 End Sub
327
328 Sub DeleteAllGarbageMemories()
329 DeleteGarbageMemories( NULL )
330 End Sub
331
332 Sub Compaction()
333 Dim i As Long, i2 = 0 As Long
334 For i=0 To ELM(n)
335 ppPtr[i2] = ppPtr[i]
336 pSize[i2] = pSize[i]
337 pdwFlags[i2] = pdwFlags[i]
338
339 If ppPtr[i] Then
340 i2++
341 End If
342 Next
343 n = i2
344 End Sub
345
346 ' スウィープ(新規スレッドで呼び出し)
347 Function Cdecl SweepOnOtherThread() As Long
[171]348 EnterCriticalSection(CriticalSection)
[144]349
[202]350 If isSweeping <> False Or iAllSize<limitMemorySize Then
351 ExitThread(0)
352 End If
353
[144]354 ' すべてのスレッドを一時停止
355 _System_pobj_AllThreads->SuspendAllThread()
356
357 ' マークリストを生成
358 Dim pbMark = HeapAlloc(_System_hProcessHeap,HEAP_ZERO_MEMORY,n*SizeOf(Byte)) As *Byte
359
360 ' グローバル領域をルートに指定してスキャン
361 Scan( _System_gc_GlobalRoot_StartPtr, _System_gc_GlobalRoot_Size, pbMark )
362
363 ' ローカル領域をルートに指定してスキャン
364 LocalScan( pbMark )
365
366 ' スウィープ前のメモリサイズを退避
367 Dim iBackAllSize = iAllSize
368
369 ' スウィープ前のメモリオブジェクトの数
370 Dim iBeforeN = n
371
372 '使われていないメモリを解放する
373 DeleteGarbageMemories(pbMark)
374
375 'コンパクション
376 Compaction()
377
[1]378 'マークリストを解放
379 HeapFree(_System_hProcessHeap,0,pbMark)
380
381 If iBackAllSize=iAllSize Then
[144]382 If iAllSize > limitMemorySize Then
383 limitMemorySize = iAllSize
384 End If
385
[1]386 '許容量を拡張する
[144]387 limitMemorySize *= 2
388
389 OutputDebugString( Ex"memory size is extended for gc!\r\n" )
[1]390 End If
391
[144]392 Dim temp[100] As Char
393 wsprintf(temp,Ex"object items ... %d -> %d ( %dMB -> %dMB )\r\n",iBeforeN,n, iBackAllSize\1024\1024, iAllSize\1024\1024)
394 OutputDebugString( temp )
395 wsprintf(temp,Ex"limit size of memory ... %d\r\n",limitMemorySize)
396 OutputDebugString( temp )
397 OutputDebugString( Ex"garbage colletion sweep finish!\r\n" )
[1]398
[144]399
[1]400 '-------------------------------------
401 ' すべてのスレッドを再開
402 '-------------------------------------
[18]403 _System_pobj_AllThreads->ResumeAllThread()
[171]404
405 LeaveCriticalSection(CriticalSection)
[1]406 End Function
407
[144]408 ' 未解放のメモリオブジェクトをトレース
409 Sub DumpMemoryLeaks()
410 Dim isLeak = False
[1]411 Dim i As Long
412 For i=0 To ELM(n)
[144]413 If ppPtr[i] Then
414 If (pdwFlags[i] and _System_GC_FLAG_NEEDFREE)<>0 Then
415 If isLeak = False Then
416 OutputDebugString( Ex"Detected memory leaks!\r\n" )
417 isLeak = True
418 End If
[1]419
[144]420 Dim temp[100] As Char
421 OutputDebugString( Ex"heap free missing!\r\n" )
422 wsprintf(temp,Ex"{%d} normal block at &H%08X, %d bytes long.\r\n", i, ppPtr[i], pSize[i])
423 OutputDebugString( temp )
[1]424 End If
425 End If
426 Next
[144]427
428 If isLeak Then
429 OutputDebugString( Ex"Object dump complete.\r\n" )
430 End If
[170]431
[1]432 End Sub
[144]433
[1]434End Class
435
436'GC管理用の特殊なシステムオブジェクト(デストラクタは最終のタイミングで呼び出されます)
[144]437Dim _System_pGC As *_System_CGarbageCollection
[1]438
439
440
441Function GC_malloc(size As Long) As VoidPtr
442 ' sweep
[144]443 _System_pGC->sweep()
[1]444
445 'allocate
[144]446 Return _System_pGC->__malloc(size,0)
[1]447End Function
448
449Function GC_malloc_atomic(size As Long) As VoidPtr
450 ' sweep
[144]451 _System_pGC->sweep()
[1]452
453 'allocate
[144]454 Return _System_pGC->__malloc(size,_System_GC_FLAG_ATOMIC)
[1]455End Function
[144]456
457Function _System_GC_malloc_ForObject(size As Long) As VoidPtr
458 ' sweep
459 _System_pGC->sweep()
460
461 'allocate
462 Return _System_pGC->__malloc(size,_System_GC_FLAG_OBJECT or _System_GC_FLAG_INITZERO)
463End Function
464
465Function _System_GC_malloc_ForObjectPtr(size As Long) As VoidPtr
466 ' sweep
467 _System_pGC->sweep()
468
469 'allocate
470 Return _System_pGC->__malloc(size,_System_GC_FLAG_OBJECT or _System_GC_FLAG_INITZERO or _System_GC_FLAG_NEEDFREE)
471End Function
472
473Function _System_GC_free_for_SweepingDelete( ptr As *Object )
474 ' free
475 _System_pGC->__free_ex( ptr, True )
476End Function
Note: See TracBrowser for help on using the repository browser.