Page 1 of 2

MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 1:18 pm
by benubi
Hello

please don't get a burn-out from reading my growing wishlist :lol:

Code: Select all

MapElementExists(Map(), Key$)
This procedure returns the *Element pointer or #Null, but does not change the current element.

Useful especially when using a shared map for reading over multiple threads :mrgreen:

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 1:19 pm
by jacdelad

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 1:21 pm
by benubi
Thanks for the tip but nope. :wink:

This function DOES change the current element. What I want is one that checks (thread safely) for the element's existence without changing the current element (which causes bad read/writes in multi-threaded, problems during ForEach etc.).

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 1:23 pm
by benubi
I know you can also use the Threaded keyword, but it won't work in an structure-embedded Map(), like I did in my failed web server experiment (ca. 900kB of source-codes).

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 1:24 pm
by jacdelad
Oh, I never thought of that especially. I thought you missed the Find-function. Sorry.

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 1:45 pm
by benubi
No worries we are all fine gentlemen :^)

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 2:05 pm
by Demivec
benubi wrote: Fri Aug 25, 2023 1:21 pm Thanks for the tip but nope. :wink:

This function DOES change the current element. What I want is one that checks (thread safely) for the element's existence without changing the current element (which causes bad read/writes in multi-threaded, problems during ForEach etc.).
As a proof of concept, here is a macro that can do the job and an unproductive demo to test it:

Code: Select all

Macro mcr_MapElementExists(_Map_, _KeyStr_, _ResultInt_)
  PushMapPosition(_Map_)
  _ResultInt_ = FindMapElement(_Map_, _KeyStr_)
  PopMapPosition(_Map_)
EndMacro

NewMap myMap.s()

myMap("one") = "first"
myMap("two") = "second"
myMap("three") = "third"
myMap("four") = "fourth"
Define order$ = "zero,one,two,three,four,five,six", k$, exists

Debug "Note: map elements are not in a sorted order"
Debug "---------------------"
ForEach myMap()
  Debug MapKey(mymap()) + " = " + mymap()
  k$ = StringField(order$, Random(CountString(order$, ",")) + 1, ",")
  mcr_MapElementExists(myMap(), k$, exists)
  If exists
    Debug "Element " + k$ + " exists."
  Else
    Debug "Element " + k$ + " does not exist."
  EndIf
Next
Sample output:

Code: Select all

Note: map elements are not in a sorted order
---------------------
two = second
Element five does not exist.
four = fourth
Element six does not exist.
one = first
Element four exists.
three = third
Element five does not exist.

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 2:26 pm
by Kurzer
Or as a Procedure...

Code: Select all

NewMap Country.s()

Procedure.i MapElementExists(Map myMap.s(), sKey.s)
	Protected.i iResult
	PushMapPosition(myMap())
	iResult = Bool(FindMapElement(myMap(), sKey)<>0)
	PopMapPosition(myMap())
	ProcedureReturn iResult
EndProcedure

Country("US") = "United States"
Country("FR") = "France"
Country("GE") = "Germany"

ResetMap(Country())
FindMapElement(Country(), "FR")
Debug Country()

Debug "GE? " + Str(MapElementExists(Country(), "GE"))
Debug "US? " + Str(MapElementExists(Country(), "US"))
Debug "XY? " + Str(MapElementExists(Country(), "XY"))

Debug Country()
[15:24:48] Warte auf den Start des Executable...
[15:24:48] Executable-Typ: Windows - x64 (64bit, Unicode)
[15:24:48] Executable gestartet.
[15:24:48] [Debug] France
[15:24:48] [Debug] GE? 1
[15:24:48] [Debug] US? 1
[15:24:48] [Debug] XY? 0
[15:24:48] [Debug] France
[15:24:48] Die Programmausführung ist abgeschlossen.

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 3:04 pm
by skywalk
Can you explain what problem this solves?
Still not completely reliable without mutex or semaphores.
If 2,3,10 or more threads run findmapelement() on same map?

Re: MapElementExists(Map(), Key$)

Posted: Fri Aug 25, 2023 5:20 pm
by Kurzer
This procedure (and also Demivec's macro) return the *element pointer or 0, but does not change the current element of the map() as FindMapelement() does. It is a kind of workaround for the feature request of the thread creator.
And yes, in a multithreaded environment you still have to secure this using mutex or semaphores.

Re: MapElementExists(Map(), Key$)

Posted: Sat Aug 26, 2023 1:41 am
by Demivec
Kurzer wrote: Fri Aug 25, 2023 2:26 pm Or as a Procedure...
There is a drawback to a procedure and that it is usable with only one type of map because of the parameter declaration. It does have the plus of not needing additional variables in the calling scope. A variation using a macro to define a procedure with the specific map element type as a kind of template would solve both those situations. I'll leave that as an exercise to the reader. :wink: It wouldn't be needed as often as the original solution, which already may be a uncommon occurrence.

Re: MapElementExists(Map(), Key$)

Posted: Sat Aug 26, 2023 5:40 pm
by netmaestro
Demivec's macro looks good to me. Am I missing something here? The macro makes no changes, just taking a peek and saying yes or no if an element is there or not. So why the need for thread safety and mutexes?

Re: MapElementExists(Map(), Key$)

Posted: Mon Aug 28, 2023 12:13 pm
by spikey
netmaestro wrote: Sat Aug 26, 2023 5:40 pm Demivec's macro looks good to me. Am I missing something here? The macro makes no changes, just taking a peek and saying yes or no if an element is there or not. So why the need for thread safety and mutexes?
Suppose two threads are running concurrently but slightly out of step, the map is currently at item 'X' before the threads act.

Code: Select all

Thread A                    Thread B
--------                    --------
PushMapPosition (at 'X')
FindMapElement (for 'Y')    PushMapPosition(at 'X', 'Y' or invalid if 'Y' is not an element?)
PopMapPosition (at 'X')     FindMapElement('Z')
                            PopMapPosition(Possibly X or Y?)
The code is fine in a single thread but in concurrent threads, because FindMapElement changes the current map pointer and the operation overall isn't performed atomically, the result in thread B and the final position are indeterminate, depending upon the difference in step between the two threads. The more threads that were running concurrently the worse this problem would get.

Threadsafe code is definitely required and a mutex could well be necessary to ensure atomicity.

Re: MapElementExists(Map(), Key$)

Posted: Mon Aug 28, 2023 4:16 pm
by netmaestro
Right, it changes the map pointer and pops it back, making threadsafety necessary.

Re: MapElementExists(Map(), Key$)

Posted: Mon Aug 28, 2023 5:51 pm
by Little John
+1 for benubi's request.