## IMPLEMENTING A BLOCKING OPERATION We're going to use 'http.get' as a prototypical example of a blocking operation. Before you do any blocking operation, you need to use a guard to see if it is actually safe to do a blocking operation. The guard always has three cases: **Case 1. Probe.** Check for !lua_isyieldable. If you're not in a yieldable context, then you're in a probe. You can't do any blocking operation at all. Throw an error. If you accidentally fail to check this, and yield anyway, the yield will generate an "attempt to yield from outside a coroutine" error (a lua built-in error). This, in turn, should trigger an assert fail. **Case 2.** **Nonauthoritative**. Check for !World::is_authoritative. Blocking operations in a nonauthoritative model should trigger a nopredict. In this case, you should call 'lua_yield(L, 0)', this will automatically be converted to a nopredict by run_scheduled_threads. In this case, you must remember that you killed the thread, and therefore, you shouldn't schedule any kind of thread wakeup - there is no thread to wake up. **Case 3.** **Authoritative.** You're in a yieldable context, in an authoritative model. You can actually do the blocking operation. These are the steps of performing a blocking operation: - Initiate the operation. For example, you might store an HTTP request in a queue of outgoing HTTP requests. - Store your place ID and thread ID so that your thread can be awakened when the operation completes. For example, you might store it in the HTTP request data structure. You can get your place and thread ID from lthread_place_id_ and lthread_thread_id_. - Execute the code 'return lua_yield(L, 0)'. This ends the blocking subroutine while pausing the thread. - When the system code determines that the blocking is complete, it must generate the return value for the blocking operation. It stores this return value by pushing it onto the stack of the sleeping thread. To find the sleeping thread, it must look up the thread's tangible, and then find the thread in the thread table of the tangible. If for some reason there's no such thread (eg, tangible has been bulldozed), simply drop the operation. - Finally, store the place ID and thread ID in the scheduler queue with time zero, and call run_scheduled_threads. ## NOPREDICT NoPredict follows a similar pattern to blocking: there are three cases. **Case 1. Probe.** Check for !lua_isyieldable. If you're not in a yieldable context, then you're in a probe. NoPredict should have no effect in this case. Do nothing. **Case 2. Authoritative.** Check for World::is_authoritative. If you're in an authoritative context, then nopredict should not do anything. Do nothing. **Case 3. Nonauthoritative.** You're in a yieldable context, in a nonauthoritative model. Call 'lua_yield(L, 0)'. This will automatically be converted to a nopredict by run_scheduled_threads. In other words, the correct code snippet for 'nopredict' is: ```cpp if (lua_isyieldable(L) && !w->is_authoritative()) return lua_yield(L, 0); } ``` When doing a nopredict, Instead of checking is_authoritative, you can check for the presence of the registry key "globaldb", or the registry key "oncedb." This avoids having to include "world.hpp"