Threads and cache cleaning now mutually excluded
This commit is contained in:
234
PBMap.pb
234
PBMap.pb
@@ -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
|
||||
|
Reference in New Issue
Block a user