Merge pull request #15 from djes/djes

Rewrite of cache mechanism
This commit is contained in:
djes
2017-06-15 16:42:15 +02:00
committed by GitHub
3 changed files with 605 additions and 466 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
PBMap.pb.bak PBMap.pb.bak
*.exe

413
PBMap.pb
View File

@@ -47,6 +47,7 @@ DeclareModule PBMap
#PB_MAP_TILE_CLEANUP = #PB_EventType_FirstCustomValue + 3 #PB_MAP_TILE_CLEANUP = #PB_EventType_FirstCustomValue + 3
Declare InitPBMap(window) Declare InitPBMap(window)
Declare SetDebugLevel(level.i)
Declare SetOption(Option.s, Value.s) Declare SetOption(Option.s, Value.s)
Declare.s GetOption(Option.s) Declare.s GetOption(Option.s)
Declare LoadOptions(PreferencesFile.s = "PBMap.prefs") Declare LoadOptions(PreferencesFile.s = "PBMap.prefs")
@@ -124,7 +125,10 @@ Module PBMap
URL.s URL.s
CacheFile.s CacheFile.s
GetImageThread.i GetImageThread.i
RetryNb.i Download.i
Time.i
Size.i
Mutex.i
EndStructure EndStructure
Structure BoundingBox Structure BoundingBox
@@ -152,8 +156,9 @@ Module PBMap
Structure ImgMemCach Structure ImgMemCach
nImage.i nImage.i
Size.i
*Tile.Tile *Tile.Tile
TimeStackPosition.i *TimeStackPtr
Alpha.i Alpha.i
EndStructure EndStructure
@@ -197,6 +202,8 @@ Module PBMap
ShowPointer.i ShowPointer.i
TimerInterval.i TimerInterval.i
MaxMemCache.i ; in MiB MaxMemCache.i ; in MiB
MaxThreads.i ; Maximum simultaneous web loading threads
MaxDownloadSlots.i ; Maximum simultaneous download slots
TileLifetime.i TileLifetime.i
Verbose.i ; Maximum debug informations Verbose.i ; Maximum debug informations
Warning.i ; Warning requesters Warning.i ; Warning requesters
@@ -281,11 +288,16 @@ Module PBMap
MemCache.TileMemCach ; Images in memory cache MemCache.TileMemCach ; Images in memory cache
ThreadsNB.i ; Current web threads nb
Mode.i ; User mode : 0 (default)->hand (moving map) and select markers, 1->hand, 2->select only (moving objects), 3->drawing (todo) Mode.i ; User mode : 0 (default)->hand (moving map) and select markers, 1->hand, 2->select only (moving objects), 3->drawing (todo)
Redraw.i Redraw.i
Dragging.i Dragging.i
Dirty.i ; To signal that drawing need a refresh Dirty.i ; To signal that drawing need a refresh
MemoryCacheAccessMutex.i ; Memorycache access variable mutual exclusion
DownloadSlots.i ; Actual nb of used download slots
List TracksList.Tracks() ; To display a GPX track List TracksList.Tracks() ; To display a GPX track
List Markers.Marker() ; To diplay marker List Markers.Marker() ; To diplay marker
EditMarker.l EditMarker.l
@@ -300,7 +312,7 @@ Module PBMap
;-*** Global variables ;-*** Global variables
;-Show debug infos ;-Show debug infos
Global MyDebugLevel = 3 Global MyDebugLevel = 5
Global PBMap.PBMap, Null.i, NullPtrMem.i, *NullPtr = @NullPtrMem Global PBMap.PBMap, Null.i, NullPtrMem.i, *NullPtr = @NullPtrMem
Global slash.s Global slash.s
@@ -344,9 +356,14 @@ Module PBMap
EndIf EndIf
EndProcedure 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) ; Send debug infos to stdout (allowing mixed debug infos with curl or other libs)
Procedure MyDebug(msg.s, DbgLevel = 0) Procedure MyDebug(msg.s, DbgLevel = 0)
If PBMap\Options\Verbose And DbgLevel >= MyDebugLevel If PBMap\Options\Verbose And DbgLevel <= MyDebugLevel
PrintN(msg) PrintN(msg)
; Debug msg ; Debug msg
EndIf EndIf
@@ -681,6 +698,10 @@ Module PBMap
PBMap\Options\HDDCachePath = Value PBMap\Options\HDDCachePath = Value
Case "maxmemcache" Case "maxmemcache"
PBMap\Options\MaxMemCache = Val(Value) PBMap\Options\MaxMemCache = Val(Value)
Case "maxthreads"
PBMap\Options\MaxThreads = Val(Value)
Case "maxdownloadslots"
PBMap\Options\MaxDownloadSlots = Val(Value)
Case "tilelifetime" Case "tilelifetime"
PBMap\Options\TileLifetime = Val(Value) PBMap\Options\TileLifetime = Val(Value)
Case "verbose" Case "verbose"
@@ -748,6 +769,10 @@ Module PBMap
ProcedureReturn \HDDCachePath ProcedureReturn \HDDCachePath
Case "maxmemcache" Case "maxmemcache"
ProcedureReturn StrU(\MaxMemCache) ProcedureReturn StrU(\MaxMemCache)
Case "maxthreads"
ProcedureReturn StrU(\MaxThreads)
Case "maxdownloadslots"
ProcedureReturn StrU(\MaxDownloadSlots)
Case "tilelifetime" Case "tilelifetime"
ProcedureReturn StrU(\TileLifetime) ProcedureReturn StrU(\TileLifetime)
Case "verbose" Case "verbose"
@@ -809,6 +834,8 @@ Module PBMap
PreferenceGroup("OPTIONS") PreferenceGroup("OPTIONS")
WritePreferenceInteger("WheelMouseRelative", \WheelMouseRelative) WritePreferenceInteger("WheelMouseRelative", \WheelMouseRelative)
WritePreferenceInteger("MaxMemCache", \MaxMemCache) WritePreferenceInteger("MaxMemCache", \MaxMemCache)
WritePreferenceInteger("MaxThreads", \MaxThreads)
WritePreferenceInteger("MaxDownloadSlots", \MaxDownloadSlots)
WritePreferenceInteger("TileLifetime", \TileLifetime) WritePreferenceInteger("TileLifetime", \TileLifetime)
WritePreferenceInteger("Verbose", \Verbose) WritePreferenceInteger("Verbose", \Verbose)
WritePreferenceInteger("Warning", \Warning) WritePreferenceInteger("Warning", \Warning)
@@ -873,6 +900,8 @@ Module PBMap
PreferenceGroup("OPTIONS") PreferenceGroup("OPTIONS")
\WheelMouseRelative = ReadPreferenceInteger("WheelMouseRelative", #True) \WheelMouseRelative = ReadPreferenceInteger("WheelMouseRelative", #True)
\MaxMemCache = ReadPreferenceInteger("MaxMemCache", 20480) ; 20 MiB, about 80 tiles in memory \MaxMemCache = ReadPreferenceInteger("MaxMemCache", 20480) ; 20 MiB, about 80 tiles in memory
\MaxThreads = ReadPreferenceInteger("MaxThreads", 40)
\MaxDownloadSlots = ReadPreferenceInteger("MaxDownloadSlots", 2)
\TileLifetime = ReadPreferenceInteger("TileLifetime", 1209600) ; about 2 weeks ;-1 = unlimited \TileLifetime = ReadPreferenceInteger("TileLifetime", 1209600) ; about 2 weeks ;-1 = unlimited
\Verbose = ReadPreferenceInteger("Verbose", #False) \Verbose = ReadPreferenceInteger("Verbose", #False)
\Warning = ReadPreferenceInteger("Warning", #False) \Warning = ReadPreferenceInteger("Warning", #False)
@@ -891,7 +920,7 @@ Module PBMap
\ColourFocus = ReadPreferenceInteger("ColourFocus", RGBA(255, 255, 0, 255)) \ColourFocus = ReadPreferenceInteger("ColourFocus", RGBA(255, 255, 0, 255))
\ColourSelected = ReadPreferenceInteger("ColourSelected", RGBA(225, 225, 0, 255)) \ColourSelected = ReadPreferenceInteger("ColourSelected", RGBA(225, 225, 0, 255))
\ColourTrackDefault = ReadPreferenceInteger("ColourTrackDefault", RGBA(0, 255, 0, 150)) \ColourTrackDefault = ReadPreferenceInteger("ColourTrackDefault", RGBA(0, 255, 0, 150))
\TimerInterval = 20 \TimerInterval = 12
ClosePreferences() ClosePreferences()
EndWith EndWith
SetOptions() SetOptions()
@@ -1021,51 +1050,73 @@ Module PBMap
ProcedureReturn PBMap\Layers(Name)\Alpha ProcedureReturn PBMap\Layers(Name)\Alpha
EndProcedure EndProcedure
;-*** These are threaded ;-***
; If cache size exceeds limit, try to delete the oldest tiles used (first in the time stack)
Procedure MemoryCacheManagement()
LockMutex(PBMap\MemoryCacheAccessMutex) ; Prevents thread to start or finish
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 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
; Is the loading over
If PBMap\MemCache\Images(CacheMapKey)\Tile <= 0 ;TODO Should not verify this var directly
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) ; Could lead to error
; 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
UnlockMutex(PBMap\MemoryCacheAccessMutex)
EndProcedure
Procedure.i GetTileFromHDD(CacheFile.s) Procedure.i GetTileFromHDD(CacheFile.s)
Protected nImage.i, LifeTime.i, MaxLifeTime.i = PBMap\Options\TileLifetime Protected nImage.i, LifeTime.i, MaxLifeTime.i
If FileSize(CacheFile) <> -1 ; Everything is OK, loads the file
;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
MyDebug("Deleting too old (" + StrU(LifeTime) + " secs) " + CacheFile, 3)
DeleteFile(CacheFile)
ProcedureReturn -1
EndIf
EndIf
;Everything is OK, load the file
nImage = LoadImage(#PB_Any, CacheFile) nImage = LoadImage(#PB_Any, CacheFile)
If IsImage(nImage) If nImage
MyDebug(" Success loading " + CacheFile + " as nImage " + Str(nImage), 3) MyDebug(" Success loading " + CacheFile + " as nImage " + Str(nImage), 3)
ProcedureReturn nImage ProcedureReturn nImage
Else Else
MyDebug(" Failed loading " + CacheFile + " as nImage " + Str(nImage) + " -> not an image !", 3) MyDebug(" Failed loading " + CacheFile + " as nImage " + Str(nImage) + " -> not an image !", 3)
If DeleteFile(CacheFile)
MyDebug(" Deleting faulty image file " + CacheFile, 3) MyDebug(" Deleting faulty image file " + CacheFile, 3)
DeleteFile(CacheFile)
EndIf
Else Else
MyDebug("Failed loading " + CacheFile + " -> Size <= 0", 3) MyDebug(" Can't delete faulty image file " + CacheFile, 3)
EndIf EndIf
ProcedureReturn -1 EndIf
ProcedureReturn #False
EndProcedure EndProcedure
Procedure.i GetTileFromWeb(TileURL.s, CacheFile.s) ; **** OLD IMPORTANT NOTICE (please not remove)
Protected *Buffer ; This original catchimage/saveimage method is a double operation (uncompress/recompress PNG)
Protected nImage.i = -1
Protected FileSize.i, timg
FileSize = ReceiveHTTPFile(TileURL, CacheFile)
If FileSize > 0
MyDebug("Loaded from web " + TileURL + " as CacheFile " + CacheFile, 3)
nImage = GetTileFromHDD(CacheFile)
Else
MyDebug("Problem loading from web " + TileURL + " as CacheFile " + CacheFile, 3)
EndIf
; **** IMPORTANT NOTICE (please not remove)
; I'm (djes) now using Curl (actually, just normal pb) only, as this original catchimage/saveimage method is a double operation (uncompress/recompress PNG)
; and is modifying the original PNG image which could lead to PNG error (Idle has spent hours debunking the 1 bit PNG bug) ; and is modifying the original PNG image which could lead to PNG error (Idle has spent hours debunking the 1 bit PNG bug)
; More than that, the original Purebasic Receive library is still not Proxy enabled. ; Protected *Buffer
; Protected nImage.i = -1
; Protected timg
; *Buffer = ReceiveHTTPMemory(TileURL) ; TODO to thread by using #PB_HTTP_Asynchronous ; *Buffer = ReceiveHTTPMemory(TileURL) ; TODO to thread by using #PB_HTTP_Asynchronous
; If *Buffer ; If *Buffer
; nImage = CatchImage(#PB_Any, *Buffer, MemorySize(*Buffer)) ; nImage = CatchImage(#PB_Any, *Buffer, MemorySize(*Buffer))
@@ -1084,106 +1135,175 @@ Module PBMap
; MyDebug(" Problem loading from web " + TileURL, 3) ; MyDebug(" Problem loading from web " + TileURL, 3)
; EndIf ; EndIf
; **** ; ****
ProcedureReturn nImage
EndProcedure ;-*** These are threaded
Threaded Progress = 0, Quit = #False
Procedure GetImageThread(*Tile.Tile) Procedure GetImageThread(*Tile.Tile)
Protected nImage.i = -1 LockMutex(PBMap\MemoryCacheAccessMutex)
MyDebug("Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " starting for image " + *Tile\CacheFile, 5)
; If MemoryCache is currently being cleaned, abort
; If PBMap\MemoryCacheAccessNB = -1
; MyDebug(" Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " for image " + *Tile\CacheFile + " canceled because of cleaning.", 5)
; *Tile\Size = 0 ; \Size = 0 signals that the download has failed
; PostEvent(#PB_Event_Gadget, PBMap\Window, PBmap\Gadget, #PB_MAP_TILE_CLEANUP, *Tile) ; To free memory outside the thread
; UnlockMutex(PBMap\MemoryCacheAccessMutex)
; ProcedureReturn
; EndIf
; We're accessing MemoryCache
UnlockMutex(PBMap\MemoryCacheAccessMutex)
*Tile\Size = 0
*Tile\Download = ReceiveHTTPFile(*Tile\URL, *Tile\CacheFile, #PB_HTTP_Asynchronous)
If *Tile\Download
Repeat Repeat
nImage = GetTileFromWeb(*Tile\URL, *Tile\CacheFile) Progress = HTTPProgress(*Tile\Download)
If nImage <> -1 Select Progress
MyDebug("Image key : " + *Tile\key + " web image loaded", 3) Case #PB_Http_Success
*Tile\RetryNb = 0 *Tile\Size = FinishHTTP(*Tile\Download) ; \Size signals that the download is OK
Else MyDebug(" Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " for image " + *Tile\CacheFile + " finished. Size : " + Str(*Tile\Size), 5)
MyDebug("Image key : " + *Tile\key + " web image not correctly loaded, will retry in 2 secs", 3) Quit = #True
Delay(2000) Case #PB_Http_Failed
*Tile\RetryNb - 1 FinishHTTP(*Tile\Download)
*Tile\Size = 0 ; \Size = 0 signals that the download has failed
MyDebug(" Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " for image " + *Tile\CacheFile + " failed.", 5)
Quit = #True
Case #PB_Http_Aborted
FinishHTTP(*Tile\Download)
*Tile\Size = 0 ; \Size = 0 signals that the download has failed
MyDebug(" Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " for image " + *Tile\CacheFile + " aborted.", 5)
Quit = #True
Default
MyDebug(" Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " for image " + *Tile\CacheFile + " downloading " + Str(Progress) + " bytes", 5)
If ElapsedMilliseconds() - *Tile\Time > 10000
MyDebug(" Thread nb " + Str(*Tile\GetImageThread) + " " + *Tile\key + " for image " + *Tile\CacheFile + " canceled after 10 seconds.", 5)
AbortHTTP(*Tile\Download)
EndIf EndIf
Until *Tile\RetryNb <= 0 EndSelect
*Tile\nImage = nImage Delay(200) ; Frees CPU
*Tile\RetryNb = -2 ;End of the thread Until Quit
EndIf
; End of the memory cache access
LockMutex(PBMap\MemoryCacheAccessMutex)
PostEvent(#PB_Event_Gadget, PBMap\Window, PBmap\Gadget, #PB_MAP_TILE_CLEANUP, *Tile) ; To free memory outside the thread PostEvent(#PB_Event_Gadget, PBMap\Window, PBmap\Gadget, #PB_MAP_TILE_CLEANUP, *Tile) ; To free memory outside the thread
UnlockMutex(PBMap\MemoryCacheAccessMutex)
EndProcedure EndProcedure
;-*** ;-***
Procedure.i GetTile(key.s, URL.s, CacheFile.s) Procedure.i GetTile(key.s, URL.s, CacheFile.s)
; Try to find the tile in memory cache. If not found, add it, try to load it from the ; MemoryCache access management
; HDD, or launch a loading thread, and try again on the next drawing loop. LockMutex(PBMap\MemoryCacheAccessMutex)
Protected img.i = -1 ; Try to find the tile in memory cache
Protected *timg.ImgMemCach = FindMapElement(PBMap\MemCache\Images(), key) Protected *timg.ImgMemCach = FindMapElement(PBMap\MemCache\Images(), key)
If *timg If *timg
MyDebug("Key : " + key + " found in memory cache", 3) MyDebug("Key : " + key + " found in memory cache", 4)
img = *timg\nImage ; Is the associated image already been loaded in memory ?
If img <> -1 If *timg\nImage
MyDebug("Image : " + img + " found in memory cache", 3) ; Yes, returns the image's nb
MyDebug(" as image " + *timg\nImage, 4)
; *** Cache management ; *** Cache management
; Move the newly used element to the last position of the time stack ; Retrieves the image in the time stack, push it to the end (to say it's the lastly used)
SelectElement(PBMap\MemCache\ImagesTimeStack(), *timg\TimeStackPosition) ChangeCurrentElement(PBMap\MemCache\ImagesTimeStack(), *timg\TimeStackPtr)
MoveElement(PBMap\MemCache\ImagesTimeStack(), #PB_List_Last) MoveElement(PBMap\MemCache\ImagesTimeStack(), #PB_List_Last)
; *timg\TimeStackPtr = LastElement(PBMap\MemCache\ImagesTimeStack())
; *** ; ***
UnlockMutex(PBMap\MemoryCacheAccessMutex)
ProcedureReturn *timg ProcedureReturn *timg
Else
; No, try to load it from HD (see below)
MyDebug(" but not the image.", 4)
EndIf EndIf
Else Else
;PushMapPosition(PBMap\MemCache\Images()) ; The tile has not been found in the cache, so creates a new cache element
;*** Cache management *timg = AddMapElement(PBMap\MemCache\Images(), key)
; if cache size exceeds limit, try to delete the oldest tile used (first in the list) If *timg = 0
Protected CacheSize = MapSize(PBMap\MemCache\Images()) * Pow(PBMap\TileSize, 2) * 4 ; Size of a tile = TileSize * TileSize * 4 bytes (RGBA) MyDebug(" Can't add a new cache element.", 4)
Protected CacheLimit = PBMap\Options\MaxMemCache * 1024 UnlockMutex(PBMap\MemoryCacheAccessMutex)
MyDebug("Cache size : " + Str(CacheSize/1024) + " / CacheLimit : " + Str(CacheLimit/1024), 4) ProcedureReturn #False
ResetList(PBMap\MemCache\ImagesTimeStack())
While NextElement(PBMap\MemCache\ImagesTimeStack()) And CacheSize > CacheLimit
Protected CacheMapKey.s = PBMap\MemCache\ImagesTimeStack()\MapKey
Protected Image = PBMap\MemCache\Images(CacheMapKey)\nImage
If IsImage(Image) ; Check if the image is valid (is a loading thread running ?)
FreeImage(Image)
MyDebug("Delete " + CacheMapKey + " As image nb " + Str(Image), 4)
DeleteMapElement(PBMap\MemCache\Images(), CacheMapKey)
DeleteElement(PBMap\MemCache\ImagesTimeStack())
CacheSize = MapSize(PBMap\MemCache\Images()) * Pow(PBMap\TileSize, 2) * 4 ; Size of a tile = TileSize * TileSize * 4 bytes (RGBA)
EndIf EndIf
Wend ; add a new time stack element at the End
LastElement(PBMap\MemCache\ImagesTimeStack()) LastElement(PBMap\MemCache\ImagesTimeStack())
;PopMapPosition(PBMap\MemCache\Images()) ; Stores the time stack ptr
AddMapElement(PBMap\MemCache\Images(), key) *timg\TimeStackPtr = AddElement(PBMap\MemCache\ImagesTimeStack())
AddElement(PBMap\MemCache\ImagesTimeStack()) If *timg\TimeStackPtr = 0
;MoveElement(PBMap\MemCache\ImagesTimeStack(), #PB_List_Last) MyDebug(" Can't add a new time stack element.", 4)
DeleteMapElement(PBMap\MemCache\Images())
UnlockMutex(PBMap\MemoryCacheAccessMutex)
ProcedureReturn #False
EndIf
; Associates the time stack element to the cache element
PBMap\MemCache\ImagesTimeStack()\MapKey = MapKey(PBMap\MemCache\Images()) PBMap\MemCache\ImagesTimeStack()\MapKey = MapKey(PBMap\MemCache\Images())
;*** MyDebug("Key : " + key + " added in memory cache", 4)
MyDebug("Key : " + key + " added in memory cache", 3)
*timg = PBMap\MemCache\Images()
*timg\nImage = -1
EndIf EndIf
If *timg\Tile = 0 ; Check if a loading thread is not running ; If there's no active download thread for this tile
MyDebug("Trying to load from HDD " + CacheFile, 3) If *timg\Tile <= 0
img = GetTileFromHDD(CacheFile.s) ; Manage tile file lifetime, delete if too old
If img <> -1 If PBMap\Options\TileLifetime <> -1
MyDebug("Key : " + key + " found on HDD", 3) If FileSize(CacheFile) > 0 ; Does the file exists ?
*timg\nImage = img If Date() - GetFileDate(CacheFile, #PB_Date_Modified) > PBMap\Options\TileLifetime ; If Lifetime > MaxLifeTime ; There's a bug with #PB_Date_Created
*timg\Alpha = 256 If DeleteFile(CacheFile)
MyDebug(" Deleting too old image file " + CacheFile, 3)
Else
MyDebug(" Can't delete too old image file " + CacheFile, 3)
UnlockMutex(PBMap\MemoryCacheAccessMutex)
ProcedureReturn #False
EndIf
EndIf
EndIf
EndIf
; Try To load it from HD
*timg\nImage = 0
*timg\Size = FileSize(CacheFile)
If *timg\Size > 0
*timg\nImage = GetTileFromHDD(CacheFile.s)
Else
MyDebug(" Failed loading from HDD " + CacheFile + " -> Filesize = " + FileSize(CacheFile), 3)
EndIf
If *timg\nImage
; Image found and loaded from HDD
*timg\Alpha = 0
UnlockMutex(PBMap\MemoryCacheAccessMutex)
ProcedureReturn *timg ProcedureReturn *timg
EndIf Else
MyDebug("Key : " + key + " not found on HDD", 3) ; If GetTileFromHDD failed, will load it (again?) from the web
;Launch a new thread If PBMap\ThreadsNB < PBMap\Options\MaxThreads
If PBMap\DownloadSlots < PBMap\Options\MaxDownloadSlots
; Launch a new web loading thread
PBMap\DownloadSlots + 1
Protected *NewTile.Tile = AllocateMemory(SizeOf(Tile)) Protected *NewTile.Tile = AllocateMemory(SizeOf(Tile))
If *NewTile If *NewTile
With *NewTile With *NewTile
*timg\Tile = *NewTile
*timg\Alpha = 0
;*timg\nImage = -1
; New tile parameters ; New tile parameters
\key = key \key = key
\URL = URL \URL = URL
\CacheFile = CacheFile \CacheFile = CacheFile
\RetryNb = 5 \nImage = 0
\nImage = -1 \Time = ElapsedMilliseconds()
MyDebug(" Creating get image thread nb " + Str(\GetImageThread) + " to get " + CacheFile, 3)
\GetImageThread = CreateThread(@GetImageThread(), *NewTile) \GetImageThread = CreateThread(@GetImageThread(), *NewTile)
If \GetImageThread
*timg\Tile = *NewTile ; There's now a loading thread
*timg\Alpha = 0
MyDebug(" Creating get image thread nb " + Str(\GetImageThread) + " to get " + CacheFile + " (key = " + key, 3)
PBMap\ThreadsNB + 1
Else
MyDebug(" Can't create get image thread to get " + CacheFile, 3)
FreeMemory(*NewTile)
EndIf
EndWith EndWith
Else Else
MyDebug(" Error, can't create a new tile loading thread", 3) MyDebug(" Error, can't allocate memory for a new tile loading thread", 3)
EndIf
Else
MyDebug(" Thread needed " + key + " for image " + CacheFile + " canceled because no free download slot.", 5)
EndIf
Else
MyDebug(" Error, maximum threads nb reached", 3)
EndIf EndIf
EndIf EndIf
ProcedureReturn *timg EndIf
UnlockMutex(PBMap\MemoryCacheAccessMutex)
ProcedureReturn #False
EndProcedure EndProcedure
Procedure DrawTiles(*Drawing.DrawingParameters, LayerName.s) Procedure DrawTiles(*Drawing.DrawingParameters, LayerName.s)
@@ -1263,7 +1383,7 @@ Module PBMap
EndSelect EndSelect
EndWith EndWith
*timg = GetTile(key, URL, CacheFile) *timg = GetTile(key, URL, CacheFile)
If *timg\nImage <> -1 If *timg And *timg\nImage
MovePathCursor(px, py) MovePathCursor(px, py)
If *timg\Alpha <= 224 If *timg\Alpha <= 224
DrawVectorImage(ImageID(*timg\nImage), *timg\Alpha * PBMap\Layers()\Alpha) DrawVectorImage(ImageID(*timg\nImage), *timg\Alpha * PBMap\Layers()\Alpha)
@@ -1737,23 +1857,23 @@ Module PBMap
VectorFont(FontID(PBMap\Font), 16) VectorFont(FontID(PBMap\Font), 16)
VectorSourceColor(RGBA(0, 0, 0, 80)) VectorSourceColor(RGBA(0, 0, 0, 80))
MovePathCursor(50, 50) MovePathCursor(50, 50)
DrawVectorText(Str(MapSize(PBMap\MemCache\Images()))) DrawVectorText("Images in cache : " + Str(MapSize(PBMap\MemCache\Images())))
MovePathCursor(50, 70) MovePathCursor(50, 70)
Protected ThreadCounter = 0 Protected ThreadCounter = 0
ForEach PBMap\MemCache\Images() ForEach PBMap\MemCache\Images()
If PBMap\MemCache\Images()\Tile <> 0 If PBMap\MemCache\Images()\Tile > 0
If IsThread(PBMap\MemCache\Images()\Tile\GetImageThread) If IsThread(PBMap\MemCache\Images()\Tile\GetImageThread)
ThreadCounter + 1 ThreadCounter + 1
EndIf EndIf
EndIf EndIf
Next Next
DrawVectorText(Str(ThreadCounter)) DrawVectorText("Threads nb : " + Str(ThreadCounter))
MovePathCursor(50, 90) MovePathCursor(50, 90)
DrawVectorText(Str(PBMap\Zoom)) DrawVectorText("Zoom : " + Str(PBMap\Zoom))
MovePathCursor(50, 110) MovePathCursor(50, 110)
DrawVectorText(StrD(*Drawing\Bounds\NorthWest\Latitude) + "," + StrD(*Drawing\Bounds\NorthWest\Longitude)) DrawVectorText("Lat-Lon 1 : " + StrD(*Drawing\Bounds\NorthWest\Latitude) + "," + StrD(*Drawing\Bounds\NorthWest\Longitude))
MovePathCursor(50, 130) MovePathCursor(50, 130)
DrawVectorText(StrD(*Drawing\Bounds\SouthEast\Latitude) + "," + StrD(*Drawing\Bounds\SouthEast\Longitude)) DrawVectorText("Lat-Lon 2 : " + StrD(*Drawing\Bounds\SouthEast\Latitude) + "," + StrD(*Drawing\Bounds\SouthEast\Longitude))
EndProcedure EndProcedure
Procedure DrawOSMCopyright(*Drawing.DrawingParameters) Procedure DrawOSMCopyright(*Drawing.DrawingParameters)
@@ -2113,11 +2233,11 @@ Module PBMap
EndIf EndIf
EndIf EndIf
If DeleteDirectory(PBMap\Options\HDDCachePath, "", #PB_FileSystem_Recursive) If DeleteDirectory(PBMap\Options\HDDCachePath, "", #PB_FileSystem_Recursive)
MyDebug("Cache in : " + PBMap\Options\HDDCachePath + " cleared") MyDebug("Cache in : " + PBMap\Options\HDDCachePath + " cleared", 3)
CreateDirectoryEx(PBMap\Options\HDDCachePath) CreateDirectoryEx(PBMap\Options\HDDCachePath)
ProcedureReturn #True ProcedureReturn #True
Else Else
MyDebug("Can't clear cache in " + PBMap\Options\HDDCachePath) MyDebug("Can't clear cache in " + PBMap\Options\HDDCachePath, 3)
ProcedureReturn #False ProcedureReturn #False
EndIf EndIf
EndProcedure EndProcedure
@@ -2221,7 +2341,7 @@ Module PBMap
Case #PB_EventType_LeftButtonDown Case #PB_EventType_LeftButtonDown
; LatLon2Pixel(@PBMap\GeographicCoordinates, @PBMap\PixelCoordinates, PBMap\Zoom) ; LatLon2Pixel(@PBMap\GeographicCoordinates, @PBMap\PixelCoordinates, PBMap\Zoom)
PBMap\Dragging = #True PBMap\Dragging = #True
;Mem cursor Coord ; Memorize cursor Coord
PBMap\MoveStartingPoint\x = CanvasMouseX PBMap\MoveStartingPoint\x = CanvasMouseX
PBMap\MoveStartingPoint\y = CanvasMouseY PBMap\MoveStartingPoint\y = CanvasMouseY
; Clip MouseX to the map range (in X, the map is infinite) ; Clip MouseX to the map range (in X, the map is infinite)
@@ -2339,20 +2459,27 @@ Module PBMap
PBMap\Dragging = #False PBMap\Dragging = #False
PBMap\Redraw = #True PBMap\Redraw = #True
Case #PB_MAP_REDRAW Case #PB_MAP_REDRAW
Debug "Redraw"
PBMap\Redraw = #True PBMap\Redraw = #True
Case #PB_MAP_RETRY Case #PB_MAP_RETRY
Debug "Reload"
PBMap\Redraw = #True PBMap\Redraw = #True
;- Tile web loading thread cleanup
; After a Web tile loading thread, clean the tile structure memory, see GetImageThread()
Case #PB_MAP_TILE_CLEANUP Case #PB_MAP_TILE_CLEANUP
*Tile = EventData() *Tile = EventData()
key = *Tile\key key = *Tile\key
;After a Web tile loading thread, clean the tile structure memory and set the image nb in the cache *Tile\Download = 0
;avoid to have threads accessing vars (and avoid mutex), see GetImageThread() If FindMapElement(PBMap\MemCache\Images(), key) <> 0
Protected timg = PBMap\MemCache\Images(key)\Tile\nImage ;Get this new tile image nb ; If the map element has not been deleted during the thread lifetime (should not occur)
PBMap\MemCache\Images(key)\nImage = timg ;store it in the cache using the key PBMap\MemCache\Images(key)\Tile = *Tile\Size
FreeMemory(PBMap\MemCache\Images(key)\Tile) ;free the data needed for the thread If *Tile\Size
PBMap\MemCache\Images(key)\Tile = 0 ;clear the data ptr PBMap\MemCache\Images(key)\Tile = -1 ; Web loading thread has finished successfully
Else
PBMap\MemCache\Images(key)\Tile = 0
EndIf
EndIf
FreeMemory(*Tile) ; Frees the data needed for the thread (*tile=PBMap\MemCache\Images(key)\Tile)
PBMap\ThreadsNB - 1
PBMap\DownloadSlots - 1
PBMap\Redraw = #True PBMap\Redraw = #True
EndSelect EndSelect
EndProcedure EndProcedure
@@ -2360,6 +2487,7 @@ Module PBMap
; Redraws at regular intervals ; Redraws at regular intervals
Procedure TimerEvents() Procedure TimerEvents()
If EventTimer() = PBMap\Timer And (PBMap\Redraw Or PBMap\Dirty) If EventTimer() = PBMap\Timer And (PBMap\Redraw Or PBMap\Dirty)
MemoryCacheManagement()
Drawing() Drawing()
EndIf EndIf
EndProcedure EndProcedure
@@ -2391,9 +2519,8 @@ Module PBMap
Protected TimeCounter = ElapsedMilliseconds() Protected TimeCounter = ElapsedMilliseconds()
Repeat Repeat
ForEach PBMap\MemCache\Images() ForEach PBMap\MemCache\Images()
If PBMap\MemCache\Images()\Tile <> 0 If PBMap\MemCache\Images()\Tile > 0
If IsThread(PBMap\MemCache\Images()\Tile\GetImageThread) If IsThread(PBMap\MemCache\Images()\Tile\GetImageThread)
PBMap\MemCache\Images()\Tile\RetryNb = 0
If ElapsedMilliseconds() - TimeCounter > 2000 If ElapsedMilliseconds() - TimeCounter > 2000
; Should not occur ; Should not occur
KillThread(PBMap\MemCache\Images()\Tile\GetImageThread) KillThread(PBMap\MemCache\Images()\Tile\GetImageThread)
@@ -2411,17 +2538,24 @@ Module PBMap
EndProcedure EndProcedure
Procedure InitPBMap(Window) Procedure InitPBMap(Window)
With PBMap
Protected Result.i Protected Result.i
PBMap\ZoomMin = 1 \ZoomMin = 1
PBMap\ZoomMax = 18 \ZoomMax = 18
PBMap\Dragging = #False \Dragging = #False
PBMap\TileSize = 256 \TileSize = 256
PBMap\Dirty = #False \Dirty = #False
PBMap\EditMarker = #False \EditMarker = #False
PBMap\Font = LoadFont(#PB_Any, "Arial", 20, #PB_Font_Bold) \Font = LoadFont(#PB_Any, "Arial", 20, #PB_Font_Bold)
PBMap\Window = Window \Window = Window
PBMap\Timer = 1 \Timer = 1
PBMap\Mode = #MODE_DEFAULT \Mode = #MODE_DEFAULT
\MemoryCacheAccessMutex = CreateMutex()
If \MemoryCacheAccessMutex = #False
MyDebug("Cannot create a mutex", 0)
End
EndIf
EndWith
LoadOptions() LoadOptions()
TechnicalImagesCreation() TechnicalImagesCreation()
SetLocation(0, 0) SetLocation(0, 0)
@@ -2592,9 +2726,10 @@ CompilerIf #PB_Compiler_IsMainFile
; Our main gadget ; Our main gadget
PBMap::InitPBMap(#Window_0) PBMap::InitPBMap(#Window_0)
PBMap::SetOption("ShowDegrees", "0") : Degrees = 0 PBMap::SetOption("ShowDegrees", "1") : Degrees = 0
PBMap::SetOption("ShowDebugInfos", "0") PBMap::SetOption("ShowDebugInfos", "1")
PBMap::SetOption("Verbose", "0") PBMap::SetDebugLevel(5)
PBMap::SetOption("Verbose", "1")
PBMap::SetOption("ShowScale", "1") PBMap::SetOption("ShowScale", "1")
PBMap::SetOption("Warning", "1") PBMap::SetOption("Warning", "1")
PBMap::SetOption("ShowMarkersLegend", "1") PBMap::SetOption("ShowMarkersLegend", "1")
@@ -2737,8 +2872,10 @@ CompilerIf #PB_Compiler_IsMainFile
CompilerEndIf CompilerEndIf
; IDE Options = PureBasic 5.60 (Windows - x64) ; IDE Options = PureBasic 5.60 (Windows - x64)
; CursorPosition = 2691 ; CursorPosition = 2552
; FirstLine = 2684 ; FirstLine = 2548
; Folding = ------------------- ; Folding = -------------------
; EnableThread ; EnableThread
; EnableXP ; EnableXP
; CompileSourceDirectory
; DisablePurifier = 1,1,1,1

View File

@@ -17,3 +17,4 @@ Thyphoon
djes djes
Idle Idle
Progi1984 Progi1984
yves86