/* ---------------------------------------------------------------- * ExecShutdownGatherWorkers * * Destroy the parallel workers. Collect all the stats after * workers are stopped, else some work done by workers won't be * accounted. * ---------------------------------------------------------------- */ static void ExecShutdownGatherWorkers(GatherState *node) { /* Shut down tuple queue readers before shutting down workers. */ if (node->reader != NULL) { int i; for (i = 0; i < node->nreaders; ++i) DestroyTupleQueueReader(node->reader[i]); node->reader = NULL; } /* Now shut down the workers. */ if (node->pei != NULL) ExecParallelFinish(node->pei); }
/* * Attempt to read a tuple from one of our parallel workers. */ static HeapTuple gather_readnext(GatherState *gatherstate) { int waitpos = gatherstate->nextreader; for (;;) { TupleQueueReader *reader; HeapTuple tup; bool readerdone; /* Make sure we've read all messages from workers. */ HandleParallelMessages(); /* Attempt to read a tuple, but don't block if none is available. */ reader = gatherstate->reader[gatherstate->nextreader]; tup = TupleQueueReaderNext(reader, true, &readerdone); /* * If this reader is done, remove it. If all readers are done, * clean up remaining worker state. */ if (readerdone) { DestroyTupleQueueReader(reader); --gatherstate->nreaders; if (gatherstate->nreaders == 0) { ExecShutdownGatherWorkers(gatherstate); return NULL; } else { memmove(&gatherstate->reader[gatherstate->nextreader], &gatherstate->reader[gatherstate->nextreader + 1], sizeof(TupleQueueReader *) * (gatherstate->nreaders - gatherstate->nextreader)); if (gatherstate->nextreader >= gatherstate->nreaders) gatherstate->nextreader = 0; if (gatherstate->nextreader < waitpos) --waitpos; } continue; } /* If we got a tuple, return it. */ if (tup) return tup; /* * Advance nextreader pointer in round-robin fashion. Note that we * only reach this code if we weren't able to get a tuple from the * current worker. We used to advance the nextreader pointer after * every tuple, but it turns out to be much more efficient to keep * reading from the same queue until that would require blocking. */ gatherstate->nextreader = (gatherstate->nextreader + 1) % gatherstate->nreaders; /* Have we visited every TupleQueueReader? */ if (gatherstate->nextreader == waitpos) { /* * If (still) running plan locally, return NULL so caller can * generate another tuple from the local copy of the plan. */ if (gatherstate->need_to_scan_locally) return NULL; /* Nothing to do except wait for developments. */ WaitLatch(MyLatch, WL_LATCH_SET, 0); CHECK_FOR_INTERRUPTS(); ResetLatch(MyLatch); } } }
/* * Store the next tuple for a given reader into the appropriate slot. * * Returns false if the reader is exhausted, and true otherwise. */ static bool gather_merge_readnext(GatherMergeState *gm_state, int reader, bool nowait) { GMReaderTupleBuffer *tuple_buffer; HeapTuple tup = NULL; /* * If we're being asked to generate a tuple from the leader, then we just * call ExecProcNode as normal to produce one. */ if (gm_state->nreaders == reader) { if (gm_state->need_to_scan_locally) { PlanState *outerPlan = outerPlanState(gm_state); TupleTableSlot *outerTupleSlot; outerTupleSlot = ExecProcNode(outerPlan); if (!TupIsNull(outerTupleSlot)) { gm_state->gm_slots[reader] = outerTupleSlot; return true; } gm_state->gm_tuple_buffers[reader].done = true; gm_state->need_to_scan_locally = false; } return false; } /* Otherwise, check the state of the relevant tuple buffer. */ tuple_buffer = &gm_state->gm_tuple_buffers[reader]; if (tuple_buffer->nTuples > tuple_buffer->readCounter) { /* Return any tuple previously read that is still buffered. */ tuple_buffer = &gm_state->gm_tuple_buffers[reader]; tup = tuple_buffer->tuple[tuple_buffer->readCounter++]; } else if (tuple_buffer->done) { /* Reader is known to be exhausted. */ DestroyTupleQueueReader(gm_state->reader[reader]); gm_state->reader[reader] = NULL; return false; } else { /* Read and buffer next tuple. */ tup = heap_copytuple(gm_readnext_tuple(gm_state, reader, nowait, &tuple_buffer->done)); /* * Attempt to read more tuples in nowait mode and store them in the * tuple array. */ if (HeapTupleIsValid(tup)) form_tuple_array(gm_state, reader); else return false; } Assert(HeapTupleIsValid(tup)); /* Build the TupleTableSlot for the given tuple */ ExecStoreTuple(tup, /* tuple to store */ gm_state->gm_slots[reader], /* slot in which to store the * tuple */ InvalidBuffer, /* buffer associated with this tuple */ true); /* pfree this pointer if not from heap */ return true; }