About Monkey 2 › Forums › Monkey 2 Development › Concurrent list modification error.
This topic contains 15 replies, has 7 voices, and was last updated by
nerobot 2 years, 4 months ago.
-
AuthorPosts
-
November 2, 2016 at 11:47 am #4722
Howdy,
I get a concurrent list modification error if I do something like this.
Monkey123456789101112Class TestEnd ClassGlobal My_Test_List:=New List<Test>Local test_object:=New TestFor Local myobject:Test=Eachin My_Test_ListMy_Test_List.Remove(myobject)NextIs this a bug or does Monkey 2 not support this as Monkey X did?
November 2, 2016 at 2:40 pm #4726You’ll need to do something along these lines:
Monkey12345Local test_objects:= My_Test_List.All()While not test_objects.AtEndlocal o:=nodes.Currenttest_objects.Erase()wendNovember 3, 2016 at 12:48 am #4732Well. I’ll give that a try…. but.
This issue is particularly confusing when converting Monkey X code. When Monkey 2 compiles code it will error on list.Remove() with no line reference in debug and just crashes the compiled application in release. So a user who does not know this is an issue is in some cases going to have a tough time figuring out what is going wrong. Believe me, I had a tough time narrowing the issue down with my large code base.
Anyway for continuities sake it might be a good idea to have Monkey 2 work as Monkey X did in this case.
November 3, 2016 at 1:04 am #4733I wont be changing this – there’s just no easy way to guarantee you’re not completely messing up a list by removing stuff will you’re iterating through it, and I’m sure there’s a fair few ‘sneaky’ bugs out there due to this.
It showed up fine in the debugger for me, I just had to click on the first non-module function in the debug view.
Here’s a super-duper fast way to remove stuff while iterating through a stack:
Monkey1234567Local put:=0For local item:=Eachin stackIf item.ShouldBeRemoved continuestack[put]=itemput+=1Nextstack.Resize( put )November 3, 2016 at 1:14 am #4735@Richard
Your code on the first post works fine as is. I tried it and it does not give any errors compiling it with the latest MX2. The error must be somewhere else in your code.November 3, 2016 at 1:19 am #4736>Your code on the first post works fine as is
The code as posted doesn’t compile, and ‘fixing’ it still leaves you with an empty list! I had to add an item to break it.
It should also be mentioned that peterigz’s solution is faster – List.Remove() needs to find the item’s node using a linear search, while it.Remove() can just remove the node directly.
November 3, 2016 at 1:20 am #4737correction I did get the error by adding an item to the list:
[/crayon]Monkey1234567891011121314151617181920[crayon-5cba82df11561784243578 inline="true" ]#Import "<std>"Using std..Class TestEnd ClassFunction Main()Global My_Test_List:=New List<Test>Local test_object:=New TestMy_Test_List.AddLast(test_object)For Local myobject:Test=Eachin My_Test_ListMy_Test_List.Remove(myobject)NextEnd FunctionNovember 3, 2016 at 1:25 am #4739I guess we are crossing posts. Hahahah!
I usually only do it like this:
[/crayon]Monkey123456789[crayon-5cba82df13e75284464502 inline="true" ]If Not list.EmptyLocal link := list.FirstNode()While link.Valuelink.Remove()link = link.SuccWendEndIfThis should be a bit faster. I suspect.
November 3, 2016 at 2:07 am #4742Does that even work? Isn’t link.Succ null after the Remove()?
Weirdly, I have grown to really dislike ‘stunt’ list processing!
And of course, List.Clear() is fastest…
November 3, 2016 at 2:13 am #4743> Does that even work? Isn’t link.Succ null after the Remove()?
You should know. you wrote it! :).
but yes it works.>And of course, List.Clear() is fastest…
yes its faster. but for my usage which is not as simple as that, mine is a better option.[/crayon]Monkey123456789101112[crayon-5cba82df189fc133671281 inline="true" ]If Not list.EmptyLocal link := list.FirstNode()While link.ValueIf link.Value.deadlink.Remove()Endiflink = link.SuccWendEndIfI don’t see that as much of a stunt as its all part of List’s public functionality.
November 3, 2016 at 8:44 pm #4755I’ll give the above samples a try. I’m cool with the fact that this this is how it has to be. I’ll adapt.
Thanks!
November 3, 2016 at 10:47 pm #4757> I’ll give the above samples a try.
The above code for iterating through a container is a bit wrong – here’a another example that also removes items as it goes:
Monkey123456Local it:= My_List.All()While Not it.AtEndLocal item:=it.CurrentIf item.dead it.Erase() Else it.Bump()WendNote that you can replace List with Stack or Deque and it will look/work the same way.
I don’t see that as much of a stunt as its all part of List’s public functionality.
But it does depend on undocumented behaviour, eg: it assumes a node can still safely be used after it’s been removed from a list, and also that the ‘head’ node has a null value so you know when you’ve reached the end of list. This is what I mean by ‘stunt’ programming – relying on undocumented implementation details. There is a correct (ie: documented) way to do what you want (see above) so IMO it makes sense to use it.
In fact, your code *does* break here on the latest version, as I cleaned up node remove so that the node could still be inserted elsewhere safely, but I think I’ll give up on try to make a ‘clean’ list class and just concentrate on trying to duplicate bmx’s/monkey1’s quirks instead!
Think it’s also time to add RemoveIf() too!
November 3, 2016 at 11:21 pm #4760What if Stack (and others) had a new method for running an operation for each item in series.
For example:
Monkey1234567891011121314151617181920212223242526272829303132'this would be a member of StackMethod Do<T>:Void(callback:Void(item:T))'get list of all itemsLocal items := ToArray()For Local index := 0 Until items.Lengthcallback(items[index])NextEnd'example codeClass ItemField name:StringMethod New(name:String)Self.name = nameEndEndFunction Main:Void()Local items := New Stack<Item>()items.Push(New Item("apple"))items.Push(New Item("orange"))items.Push(New Item("tree"))items.Push(New Item("keyboard"))'slow but works!items.Do(Lambda(item:Item)items.Remove(item)End)EndThis could be used for a lot more then just removing!
Monkey123456789101112131415161718192021222324252627'example codeClass ItemField name:StringField hitPoints:IntMethod New(name:String, hitPoints:Int)Self.name = nameSelf.hitPoints = hitPointsEndEndFunction Main:Void()Local items := New Stack<Item>()items.Push(New Item("apple", 0))items.Push(New Item("orange", 3))items.Push(New Item("tree", 2))items.Push(New Item("keyboard", 0))Local text:Stringitems.Do(Lambda(item:Item)If item.hitPoints <= 0text+= ", "+item.name+" is dead"EndifEnd)Print textEndAlso clearly this is not the most efficient way to iterate and do stuff, but it works.
In these example the Do creates a list of items at the time of calling, so this is then safe to iterate over. It generates junk, but this could be optimised. Or a safe/unsafe flag could be specified, causing the loop to iterate directly, or create the array first with safe mode. e.g.
Monkey12345678910111213141516Method Do<T>:Void(callback:Void(item:T),safe:bool= False)If safe'iterate safe'get list of all itemsLocal items := ToArray()For Local index := 0 Until items.Lengthcallback(items[index])NextElse'iterate unsafeFor Local index := 0 Until _lengthcallback(_data[index])NextEndifEndAnother example:
Monkey123456789101112131415161718192021222324252627282930313233Class ItemField name:StringField hitPoints:IntMethod New(name:String, hitPoints:Int)Self.name = nameSelf.hitPoints = hitPointsEndEndFunction Main:Void()Local items := New Stack<Item>()items.Push(New Item("apple", 0))items.Push(New Item("orange", 3))items.Push(New Item("tree", 2))items.Push(New Item("keyboard", 0))Local currentOperation: = Operation1items.Do(currentOperation)EndFunction Operation1:Void(item:Item)item.name = "NO"EndFunction Operation2:Void(item:Item)item.name = "YES"EndFunction Operation3:Void(item:Item)item.name = "MAYBE"EndNovember 4, 2016 at 12:11 pm #4771but I think I’ll give up on try to make a ‘clean’ list class and just concentrate on trying to duplicate bmx’s/monkey1’s quirks instead!
Applause.
I look at this issue as how best can MX2 provide continuity and how can MX2 provide intensives for BlitzMax users to climb on-board. One way is by including often used legacy commands and syntax where possible. I’m not selling the idea of backpedaling but making a case for simpler conversions of legacy code.
I’m in the middle of solidifying my converted code base, but when that is done I intend to explore Stac’s and determine where best to use them and what limitations they hold. I like List’s and when used properly it is a nice feature to have. After converting a ton of legacy code Monkey 2 has proven to be friendly to the process and support of List’s and some of the past quirks is a good Idea to further smoother legacy conversions.
November 29, 2016 at 2:18 pm #5486How would someone implement an extention to a container while iterating through the same? Simple .AddLast or .Push/.Add?
-
AuthorPosts
You must be logged in to reply to this topic.