This function will check the moduleUnblockedClients queue in order to * call the reply callback and really unblock the client. * * Clients end into this list because of calls to RM_UnblockClient(), * however it is possible that while the module was doing work for the * blocked client, it was terminated by Redis (for timeout or other reasons). * When this happens the RedisModuleBlockedClient
| 5563 | * When this happens the RedisModuleBlockedClient structure in the queue |
| 5564 | * will have the 'client' field set to NULL. */ |
| 5565 | void moduleHandleBlockedClients(void) { |
| 5566 | listNode *ln; |
| 5567 | RedisModuleBlockedClient *bc; |
| 5568 | |
| 5569 | pthread_mutex_lock(&moduleUnblockedClientsMutex); |
| 5570 | /* Here we unblock all the pending clients blocked in modules operations |
| 5571 | * so we can read every pending "awake byte" in the pipe. */ |
| 5572 | char buf[1]; |
| 5573 | while (read(server.module_blocked_pipe[0],buf,1) == 1); |
| 5574 | while (listLength(moduleUnblockedClients)) { |
| 5575 | ln = listFirst(moduleUnblockedClients); |
| 5576 | bc = ln->value; |
| 5577 | client *c = bc->client; |
| 5578 | listDelNode(moduleUnblockedClients,ln); |
| 5579 | pthread_mutex_unlock(&moduleUnblockedClientsMutex); |
| 5580 | |
| 5581 | /* Release the lock during the loop, as long as we don't |
| 5582 | * touch the shared list. */ |
| 5583 | |
| 5584 | /* Call the reply callback if the client is valid and we have |
| 5585 | * any callback. However the callback is not called if the client |
| 5586 | * was blocked on keys (RM_BlockClientOnKeys()), because we already |
| 5587 | * called such callback in moduleTryServeClientBlockedOnKey() when |
| 5588 | * the key was signaled as ready. */ |
| 5589 | uint64_t reply_us = 0; |
| 5590 | if (c && !bc->blocked_on_keys && bc->reply_callback) { |
| 5591 | RedisModuleCtx ctx = REDISMODULE_CTX_INIT; |
| 5592 | ctx.flags |= REDISMODULE_CTX_BLOCKED_REPLY; |
| 5593 | ctx.blocked_privdata = bc->privdata; |
| 5594 | ctx.blocked_ready_key = NULL; |
| 5595 | ctx.module = bc->module; |
| 5596 | ctx.client = bc->client; |
| 5597 | ctx.blocked_client = bc; |
| 5598 | monotime replyTimer; |
| 5599 | elapsedStart(&replyTimer); |
| 5600 | bc->reply_callback(&ctx,(void**)c->argv,c->argc); |
| 5601 | reply_us = elapsedUs(replyTimer); |
| 5602 | moduleFreeContext(&ctx); |
| 5603 | } |
| 5604 | /* Update stats now that we've finished the blocking operation. |
| 5605 | * This needs to be out of the reply callback above given that a |
| 5606 | * module might not define any callback and still do blocking ops. |
| 5607 | */ |
| 5608 | if (c && !bc->blocked_on_keys) { |
| 5609 | updateStatsOnUnblock(c, bc->background_duration, reply_us); |
| 5610 | } |
| 5611 | |
| 5612 | /* Free privdata if any. */ |
| 5613 | if (bc->privdata && bc->free_privdata) { |
| 5614 | RedisModuleCtx ctx = REDISMODULE_CTX_INIT; |
| 5615 | if (c == NULL) |
| 5616 | ctx.flags |= REDISMODULE_CTX_BLOCKED_DISCONNECTED; |
| 5617 | ctx.blocked_privdata = bc->privdata; |
| 5618 | ctx.module = bc->module; |
| 5619 | ctx.client = bc->client; |
| 5620 | bc->free_privdata(&ctx,bc->privdata); |
| 5621 | moduleFreeContext(&ctx); |
| 5622 | } |
no test coverage detected