This isn't really specific to Lua, but since I'm doing it in Lua, and people on this list seem to know these things pretty well, I'll ask here.
I'm implementing a system which allows a Lua script to create a new OS thread. That thread creates its own, independent Lua script and runs some code given to it at creation time. The two threads need to be able to communicate with eachother. The new thread can create more new threads as well. This is a module that needs to work under standard Lua, so unfortunately I can't take advantage of lua_lock and lua_unlock macros.
What I'm having trouble with is passing messages between threads. The interface is simple enough: when you have a reference to a thread (because you created it or it was given to you as a parameter), you can send a message (a string) to it. The message goes into a queue and the thread can call a function to retrieve the next message from the queue.
Of course one thread can't touch another thread's Lua state, since a state can only be used by one thread at a time. So we need to store the received messages somewhere until the receiving thread asks for them. No problem, we have a struct for each thread already (holding its lua_State* and other nice things); we can copy the string into a list in that struct, using mutexes to ensure it's not being read and written at the same time.
The issue with that design, though, is excess copying. If thread A sends a message to thread B, that means A has a copy of the string, B's incoming message queue will have another copy, and B's Lua state will make a third copy when lua_pushlstring() is used to receive the message. I'd quite like to eliminate that extra copy.
Alright, so we don't copy the message into B's queue; we give it a pointer to the string in A's Lua state, and anchor it in the registry to keep it from being collected before B receives the message. Now B copies directly from A, no third copy, no problem?
Well, two problems actually. Those strings have to be removed from A's registry sometime so they aren't wasting space, and if A exits before B checks its messages, the string is still going to get collected and the pointer will be invalid.
So we'll have B notify A in some fashion when it receives a message, so that A can allow the message to be collected. Maybe we'll keep the ID from luaL_ref in the message struct as well, and then B can add that ID to a list in A's thread struct (the same way A adds the message to B's message queue) telling it that the message is now safe to collect. Then A can check that list periodically to collect messages, and can wait before exiting to ensure all messages are received.
But, what if B never checks its messages? Perhaps B is in a loop waiting for some other event that never comes? Then A will sit waiting forever before it's allowed to exit, waiting for those sent messages to become collectible, which will never happen.