List out of bounds in for statement with range maximum equal to length of list...

Started by
8 comments, last by Dan Bilkey 7 years, 6 months ago

I was wondering if anyone can give me some insight on why I might be getting this occasional error.


for i in range(0, len(gameState.items)):
     if gameState.items[i].idNum > 0:

The second line on rare occassion throws an error "List out of bounds", which shouldn't be possible as the iterative variable 'i' should not be able to exceed the maximum list size due to the len() constraint in the 'for i in range' statement.

The only way I thought this might be occurring is that the client thread that receives network data from the server may be acting upon the 'gameState.items[]' variable, but I have set a threadLock() on that thread while it is modifying variable data.

Anyone have any thoughts on why this might be happening?

Advertisement
Is it possible that the length of gameState.items is being changed while in the loop?

That's what I thought too, but the only thing that modifies the length of gameState.items is the thread receiving data from the server.
So I invoked a 'threadLock.acquire()' on that thread before it takes action on any packets received.

The for loop I quoted in the original post is in the programs main loop (not a thread), maybe a threadLock doesnt create processor priority when there is a non-thread process involved?

Do you also lock in the main thread before you enter the loop? Otherwise I suspect there is some big hole in your understanding of threading synchronization (not that I am an expert myself).

The main loop isnt a thread, here is psuedo code for how I have it set up:


thread getData:
    getDataFromServer
    threadLock()
    takeActionOnData
    releaseThreadLock()

#Main game loop
while userLoggedIn:
    handleHardwareInput()
    renderGraphics()

To my knowledge in Python threads work by taking turns gaining control of the CPU (1 thread at a time because of GIL).
My thought was to lock the 'getData' thread so it is the only process running on the CPU and after it has modified variable data unlock it.
I'm assuming my error here is because the "main loop" isn't considered to be a thread, threadLock() isnt preventing the 'main loop' from running while the 'getData' thread is updating variable data.

I'm assuming I could prevent the error easily by creating a global variable 'isMainLoopLockedOut' that does not allow the main loop to iterate while True, but I feel like solutions like that are merely 'bandaids' per say and don't treat the root problem. If in the case that I can't solve the problem without significant re-design of the client i'll likely use this solution, I just don't like that idea heh

I bet the range statement is returning a list of number(s) and jumping in to the for loop when len(gameState.items) is 0. If you only saw it when that list was empty it would explain the sporadic nature of it. Wrap it in an if check of the gameState.items list not being empty and see if that helps.

Edit:

Scratch that, here is better approach:


for state in gameState.items:
     if state.idNum > 0:

This will iterate through any item in the list by returning that object directly for manipulation. Much cleaner.

To my knowledge in Python threads work by taking turns gaining control of the CPU (1 thread at a time because of GIL).
Well, yes, but that only means there is 1 thread running. It does imho not mean the main thread can't be kicked from the CPU, eg when a packet arrives from the network. As such I would expect you need to protect the list from getting modified while iterating over it.

You may want to make a shallow copy while being protected (assuming the network only modifies the list, and not the element contents), before you start iterating.

Best answer if you may be getting concurrent modification of items in list is not to iterate at all. Instead, a while loop that runs as long as the list isn't empty might be more suitable. It won't care if the list length value changes mid-loop. Can anywhere else in code remove items from this list besides here? If so, you should look at if you can make it only happen here.


while (list.len > 0)
  remove item at index 0
  process removed item
  destroy item
endloop

Edit: Changed loop.len to list.len

The for loop I quoted in the original post is in the programs main loop (not a thread)


If it's not in a thread then it's not running on a computer. Your program starts in a thread and may or may not spawn additional threads. If you want a mutex (a "lock") to work then you need to acquire the lock before accessing the shared resource from anywhere and remember to release it when you're done in every case.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

Thank you for all the valuable feedback everyone!
I have a good idea on how to prevent issues like this from happening going forward :)

This topic is closed to new replies.

Advertisement