Threads and cache cleaning now mutually excluded

This commit is contained in:
djes
2017-06-14 10:39:03 +02:00
parent da8c3e9001
commit 7d351f4f92

234
PBMap.pb
View File

@@ -47,6 +47,7 @@ DeclareModule PBMap
#PB_MAP_TILE_CLEANUP = #PB_EventType_FirstCustomValue + 3
Declare InitPBMap(window)
Declare SetDebugLevel(level.i)
Declare SetOption(Option.s, Value.s)
Declare.s GetOption(Option.s)
Declare LoadOptions(PreferencesFile.s = "PBMap.prefs")
@@ -123,7 +124,7 @@ Module PBMap
key.s
URL.s
CacheFile.s
*GetImageThread
GetImageThread.i
Download.i
Time.i
Size.i
@@ -292,15 +293,12 @@ Module PBMap
Dragging.i
Dirty.i ; To signal that drawing need a refresh
*MemoryCacheManagementThread
ResourceAccessSemaphore.i ; To pause web loading threads
ReadCountAccessSemaphore.i
ServiceQueueSemaphore.i
ReadCount.i
MemoryCacheAccessNB.i ; Count the access to the memory cache. =0 no access ; >0 download threads ; -1 cleaning
MemoryCacheAccessNBMutex.i ; Memorycache access variable mutual exclusion
DownloadSlots.i ; Actual nb of used download slots
DownloadSlotsMutex.i ; To be sure that only one thread at a time can access to the DownloadSlots var
List TracksList.Tracks() ; To display a GPX track
List Markers.Marker() ; To diplay marker
EditMarker.l
@@ -315,7 +313,7 @@ Module PBMap
;-*** Global variables
;-Show debug infos
Global MyDebugLevel = 3
Global MyDebugLevel = 5
Global PBMap.PBMap, Null.i, NullPtrMem.i, *NullPtr = @NullPtrMem
Global slash.s
@@ -359,9 +357,14 @@ Module PBMap
EndIf
EndProcedure
; Set the debug level allowing more or less debug infos
Procedure SetDebugLevel(level.i)
MyDebugLevel = level
EndProcedure
; Send debug infos to stdout (allowing mixed debug infos with curl or other libs)
Procedure MyDebug(msg.s, DbgLevel = 0)
If PBMap\Options\Verbose And DbgLevel >= MyDebugLevel
If PBMap\Options\Verbose And DbgLevel <= MyDebugLevel
PrintN(msg)
; Debug msg
EndIf
@@ -1050,60 +1053,63 @@ Module PBMap
;-***
Procedure MemoryCacheManagement(*Void)
With PBMap
WaitSemaphore(\ServiceQueueSemaphore) ;serviceQueue.P(); // wait in line to be serviced
WaitSemaphore(\ResourceAccessSemaphore) ;resourceAccess.P(); // request exclusive access to resource
SignalSemaphore(\ServiceQueueSemaphore) ;serviceQueue.V(); // let next in line be serviced
EndWith
; If cache size exceeds limit, try to delete the oldest tiles used (first in the time stack)
Protected CacheSize = MapSize(PBMap\MemCache\Images()) * Pow(PBMap\TileSize, 2) * 4 ; Size of a tile = TileSize * TileSize * 4 bytes (RGBA)
Protected CacheLimit = PBMap\Options\MaxMemCache * 1024
MyDebug("Cache size : " + Str(CacheSize/1024) + " / CacheLimit : " + Str(CacheLimit/1024), 5)
If CacheSize > CacheLimit
;PBMap\MemoryCacheManagement = #True
MyDebug(" Cache full. Trying cache cleaning", 5)
ResetList(PBMap\MemCache\ImagesTimeStack())
; Try to free half the cache memory (one pass)
While NextElement(PBMap\MemCache\ImagesTimeStack()) And CacheSize > (CacheLimit / 2) ; /2 = half
Protected CacheMapKey.s = PBMap\MemCache\ImagesTimeStack()\MapKey
;Protected Image = PBMap\MemCache\Images(CacheMapKey)\nImage
; Is the loading over
If PBMap\MemCache\Images(CacheMapKey)\Tile = -1
MyDebug(" Delete " + CacheMapKey, 5)
If PBMap\MemCache\Images(CacheMapKey)\nImage;IsImage(PBMap\MemCache\Images(CacheMapKey)\nImage)
FreeImage(PBMap\MemCache\Images(CacheMapKey)\nImage)
MyDebug(" and free image nb " + Str(PBMap\MemCache\Images(CacheMapKey)\nImage), 5)
PBMap\MemCache\Images(CacheMapKey)\nImage = 0
EndIf
DeleteMapElement(PBMap\MemCache\Images(), CacheMapKey)
DeleteElement(PBMap\MemCache\ImagesTimeStack(), 1)
ElseIf PBMap\MemCache\Images(CacheMapKey)\Tile = 0
MyDebug(" Delete " + CacheMapKey, 5)
DeleteMapElement(PBMap\MemCache\Images(), CacheMapKey)
DeleteElement(PBMap\MemCache\ImagesTimeStack(), 1)
ElseIf PBMap\MemCache\Images(CacheMapKey)\Tile > 0
; If the thread is running, try to abort the download
If PBMap\MemCache\Images(CacheMapKey)\Tile\Download
AbortHTTP(PBMap\MemCache\Images(CacheMapKey)\Tile\Download)
EndIf
EndIf
CacheSize = MapSize(PBMap\MemCache\Images()) * Pow(PBMap\TileSize, 2) * 4 ; Size of a tile = TileSize * TileSize * 4 bytes (RGBA)
Wend
;PBMap\MemoryCacheManagement = #False
MyDebug(" New cache size : " + Str(CacheSize/1024) + " / CacheLimit : " + Str(CacheLimit/1024), 5)
Procedure MemoryCacheManagement()
; MemoryCache access management
LockMutex(PBMap\MemoryCacheAccessNBMutex)
; If MemoryCache is not being used by a download thread
If PBMap\MemoryCacheAccessNB = 0
PBMap\MemoryCacheAccessNB = -1 ; Not really useful as the download thread are now blocked by the mutex, and this procedure is synchronous
UnlockMutex(PBMap\MemoryCacheAccessNBMutex)
; If cache size exceeds limit, try to delete the oldest tiles used (first in the time stack)
Protected CacheSize = MapSize(PBMap\MemCache\Images()) * Pow(PBMap\TileSize, 2) * 4 ; Size of a tile = TileSize * TileSize * 4 bytes (RGBA)
Protected CacheLimit = PBMap\Options\MaxMemCache * 1024
MyDebug("Cache size : " + Str(CacheSize/1024) + " / CacheLimit : " + Str(CacheLimit/1024), 5)
If CacheSize > CacheLimit
MyDebug(" Cache cleaning unsuccessfull, can't add new tiles.", 5)
MyDebug(" Cache full. Trying cache cleaning", 5)
ResetList(PBMap\MemCache\ImagesTimeStack())
; Try to free half the cache memory (one pass)
While NextElement(PBMap\MemCache\ImagesTimeStack()) And CacheSize > (CacheLimit / 2) ; /2 = half
Protected CacheMapKey.s = PBMap\MemCache\ImagesTimeStack()\MapKey
;Protected Image = PBMap\MemCache\Images(CacheMapKey)\nImage
; Is the loading over
If PBMap\MemCache\Images(CacheMapKey)\Tile = -1
MyDebug(" Delete " + CacheMapKey, 5)
If PBMap\MemCache\Images(CacheMapKey)\nImage;IsImage(PBMap\MemCache\Images(CacheMapKey)\nImage)
FreeImage(PBMap\MemCache\Images(CacheMapKey)\nImage)
MyDebug(" and free image nb " + Str(PBMap\MemCache\Images(CacheMapKey)\nImage), 5)
PBMap\MemCache\Images(CacheMapKey)\nImage = 0
EndIf
DeleteMapElement(PBMap\MemCache\Images(), CacheMapKey)
DeleteElement(PBMap\MemCache\ImagesTimeStack(), 1)
ElseIf PBMap\MemCache\Images(CacheMapKey)\Tile = 0
MyDebug(" Delete " + CacheMapKey, 5)
DeleteMapElement(PBMap\MemCache\Images(), CacheMapKey)
DeleteElement(PBMap\MemCache\ImagesTimeStack(), 1)
ElseIf PBMap\MemCache\Images(CacheMapKey)\Tile > 0
; If the thread is running, try to abort the download
If PBMap\MemCache\Images(CacheMapKey)\Tile\Download
AbortHTTP(PBMap\MemCache\Images(CacheMapKey)\Tile\Download)
EndIf
EndIf
CacheSize = MapSize(PBMap\MemCache\Images()) * Pow(PBMap\TileSize, 2) * 4 ; Size of a tile = TileSize * TileSize * 4 bytes (RGBA)
Wend
MyDebug(" New cache size : " + Str(CacheSize/1024) + " / CacheLimit : " + Str(CacheLimit/1024), 5)
If CacheSize > CacheLimit
MyDebug(" Cache cleaning unsuccessfull, can't add new tiles.", 5)
EndIf
EndIf
; We're no more accessing MemoryCache
LockMutex(PBMap\MemoryCacheAccessNBMutex)
PBMap\MemoryCacheAccessNB = 0 ; Not really useful as the download thread are now blocked
EndIf
SignalSemaphore(PBMap\ResourceAccessSemaphore) ; resourceAccess.V(); // release resource access for next reader/writer
UnlockMutex(PBMap\MemoryCacheAccessNBMutex)
EndProcedure
Procedure.i GetTileFromHDD(CacheFile.s)
Protected nImage.i, LifeTime.i, MaxLifeTime.i
MaxLifeTime.i = PBMap\Options\TileLifetime
If FileSize(CacheFile) > 0 ; <> -1
; Manage tile file lifetime
; Manage tile file lifetime
If MaxLifeTime <> -1
LifeTime = Date() - GetFileDate(CacheFile, #PB_Date_Modified) ; There's a bug with #PB_Date_Created
If LifeTime > MaxLifeTime
@@ -1156,23 +1162,13 @@ Module PBMap
; EndIf
; ****
;-*** These are threaded
;-*** These are threaded
Threaded Progress = 0, Size = 0, Quit.i = #False
Threaded Progress = 0, Size = 0, Quit = #False
Procedure GetImageThread(*Tile.Tile)
With PBMap
WaitSemaphore(\ServiceQueueSemaphore) ;serviceQueue.P(); // wait in line to be serviced
WaitSemaphore(\ReadCountAccessSemaphore) ;readCountAccess.P(); // request exclusive access to readCount
If \ReadCount = 0 ;If (readCount == 0) // If there are no readers already reading:
WaitSemaphore(\ResourceAccessSemaphore) ; resourceAccess.P(); // request resource access for readers (writers blocked)
EndIf
\ReadCount + 1 ;readCount++; // update count of active readers
SignalSemaphore(\ServiceQueueSemaphore) ;serviceQueue.V(); // let next in line be serviced
SignalSemaphore(\ReadCountAccessSemaphore) ;readCountAccess.V(); // release access to readCount
EndWith
MyDebug("Thread starting for image " + *Tile\CacheFile + "(" + *Tile\key + ")", 5)
; Waits for a free download slot
;*** Waits for a free download slot
LockMutex(PBMap\DownloadSlotsMutex)
While PBMap\DownloadSlots >= PBMap\Options\MaxDownloadSlots
UnlockMutex(PBMap\DownloadSlotsMutex)
@@ -1183,11 +1179,23 @@ Module PBMap
ProcedureReturn #False
EndIf
MyDebug(" Thread for image " + *Tile\CacheFile + " waiting a download slot", 5)
Delay(500)
Delay(20)
LockMutex(PBMap\DownloadSlotsMutex)
Wend
PBMap\DownloadSlots + 1
UnlockMutex(PBMap\DownloadSlotsMutex)
;***
; MemoryCache access management
LockMutex(PBMap\MemoryCacheAccessNBMutex)
; If MemoryCache is currently being cleaned, wait
While PBMap\MemoryCacheAccessNB = -1
UnlockMutex(PBMap\MemoryCacheAccessNBMutex)
Delay(20)
LockMutex(PBMap\MemoryCacheAccessNBMutex)
Wend
; We're accessing MemoryCache
PBMap\MemoryCacheAccessNB + 1
UnlockMutex(PBMap\MemoryCacheAccessNBMutex)
*Tile\Download = ReceiveHTTPFile(*Tile\URL, *Tile\CacheFile, #PB_HTTP_Asynchronous)
If *Tile\Download
Repeat
@@ -1215,22 +1223,18 @@ Module PBMap
AbortHTTP(*Tile\Download)
EndIf
EndSelect
;EndIf
Delay(500) ; Frees CPU
Delay(200) ; Frees CPU
Until Quit
LockMutex(PBMap\DownloadSlotsMutex)
PBMap\DownloadSlots - 1
UnlockMutex(PBMap\DownloadSlotsMutex)
PostEvent(#PB_Event_Gadget, PBMap\Window, PBmap\Gadget, #PB_MAP_TILE_CLEANUP, *Tile) ; To free memory outside the thread
EndIf
With PBMap
WaitSemaphore(\ReadCountAccessSemaphore) ;readCountAccess.P(); // request exclusive access to readCount
\ReadCount - 1 ;readCount--; // update count of active readers
If \ReadCount = 0 ;If (readCount == 0) // If there are no readers left:
SignalSemaphore(\ResourceAccessSemaphore) ; resourceAccess.V(); // release resource access for all
EndIf
SignalSemaphore(\ReadCountAccessSemaphore) ;readCountAccess.V();
EndWith
; End of the memory cache access
LockMutex(PBMap\MemoryCacheAccessNBMutex)
PBMap\MemoryCacheAccessNB - 1
UnlockMutex(PBMap\MemoryCacheAccessNBMutex)
; Frees a download slot
LockMutex(PBMap\DownloadSlotsMutex)
PBMap\DownloadSlots - 1
UnlockMutex(PBMap\DownloadSlotsMutex)
PostEvent(#PB_Event_Gadget, PBMap\Window, PBmap\Gadget, #PB_MAP_TILE_CLEANUP, *Tile) ; To free memory outside the thread
EndProcedure
;-***
@@ -2478,24 +2482,19 @@ Module PBMap
PBMap\Redraw = #True
Case #PB_MAP_RETRY
PBMap\Redraw = #True
;- Tile cleanup
;- Tile web loading thread cleanup
; After a Web tile loading thread, clean the tile structure memory, see GetImageThread()
Case #PB_MAP_TILE_CLEANUP
*Tile = EventData()
key = *Tile\key
; After a Web tile loading thread, clean the tile structure memory, see GetImageThread()
*Tile\Download = 0
;Debug key + " cleanup event"
If *Tile\Size ; <> 0
FreeMemory(PBMap\MemCache\Images(key)\Tile) ; Frees the data needed for the thread
FreeMemory(*Tile) ; Frees the data needed for the thread (*tile=PBMap\MemCache\Images(key)\Tile)
PBMap\MemCache\Images(key)\Tile = -1 ; Clears the data ptr, and says that the web loading thread has finished successfully
Else
FreeMemory(PBMap\MemCache\Images(key)\Tile) ; Frees the data needed for the thread
FreeMemory(*Tile) ; Frees the data needed for the thread (*tile=PBMap\MemCache\Images(key)\Tile)
PBMap\MemCache\Images(key)\Tile = 0 ; Clears the data ptr, and says that the web loading thread has finished unsuccessfully
EndIf
;Debug "=" + Str(PBMap\MemCache\Images(key)\Tile)
;Protected timg = PBMap\MemCache\Images(key)\Tile\nImage ; Get this new tile image nb
;PBMap\MemCache\Images(key)\nImage = timg ; Stores it in the cache using the key
PBMap\ThreadsNB - 1
PBMap\Redraw = #True
EndSelect
@@ -2504,8 +2503,8 @@ Module PBMap
; Redraws at regular intervals
Procedure TimerEvents()
If EventTimer() = PBMap\Timer And (PBMap\Redraw Or PBMap\Dirty)
MemoryCacheManagement()
Drawing()
PBMap\MemoryCacheManagementThread = CreateThread(@MemoryCacheManagement(), Null)
EndIf
EndProcedure
@@ -2532,7 +2531,6 @@ Module PBMap
Procedure Quit()
PBMap\Drawing\End = #True
;PBMap\MemoryCacheManagement = #True ; Tells web loading threads to pause
; Wait for loading threads to finish nicely. Passed 2 seconds, kills them.
Protected TimeCounter = ElapsedMilliseconds()
Repeat
@@ -2556,25 +2554,30 @@ Module PBMap
EndProcedure
Procedure InitPBMap(Window)
Protected Result.i
PBMap\ZoomMin = 1
PBMap\ZoomMax = 18
PBMap\Dragging = #False
PBMap\TileSize = 256
PBMap\Dirty = #False
PBMap\EditMarker = #False
PBMap\Font = LoadFont(#PB_Any, "Arial", 20, #PB_Font_Bold)
PBMap\Window = Window
PBMap\Timer = 1
PBMap\Mode = #MODE_DEFAULT
PBMap\DownloadSlotsMutex = CreateMutex()
PBMap\ResourceAccessSemaphore = CreateSemaphore(1)
PBMap\ReadCountAccessSemaphore = CreateSemaphore(1)
PBMap\ServiceQueueSemaphore = CreateSemaphore(1)
If PBMap\DownloadSlotsMutex = #False
MyDebug("Cannot create a mutex", 0)
End
EndIf
With PBMap
Protected Result.i
\ZoomMin = 1
\ZoomMax = 18
\Dragging = #False
\TileSize = 256
\Dirty = #False
\EditMarker = #False
\Font = LoadFont(#PB_Any, "Arial", 20, #PB_Font_Bold)
\Window = Window
\Timer = 1
\Mode = #MODE_DEFAULT
\DownloadSlotsMutex = CreateMutex()
If \DownloadSlotsMutex = #False
MyDebug("Cannot create a mutex", 0)
End
EndIf
\MemoryCacheAccessNB = 0
\MemoryCacheAccessNBMutex = CreateMutex()
If \MemoryCacheAccessNBMutex = #False
MyDebug("Cannot create a mutex", 0)
End
EndIf
EndWith
LoadOptions()
TechnicalImagesCreation()
SetLocation(0, 0)
@@ -2747,7 +2750,8 @@ CompilerIf #PB_Compiler_IsMainFile
PBMap::InitPBMap(#Window_0)
PBMap::SetOption("ShowDegrees", "1") : Degrees = 0
PBMap::SetOption("ShowDebugInfos", "1")
PBMap::SetOption("Verbose", "1")
PBMap::SetDebugLevel(4)
PBMap::SetOption("Verbose", "0")
PBMap::SetOption("ShowScale", "1")
PBMap::SetOption("Warning", "1")
PBMap::SetOption("ShowMarkersLegend", "1")
@@ -2890,8 +2894,8 @@ CompilerIf #PB_Compiler_IsMainFile
CompilerEndIf
; IDE Options = PureBasic 5.60 (Windows - x64)
; CursorPosition = 1231
; FirstLine = 1151
; CursorPosition = 2751
; FirstLine = 2738
; Folding = -------------------
; EnableThread
; EnableXP