The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
Data Structures | Macros | Enumerations | Functions | Variables
pipeline.c File Reference

Functions for pipelining commands. More...

#include <freeradius-devel/server/connection.h>
#include <freeradius-devel/server/trunk.h>
#include "pipeline.h"
#include "cluster_async.h"
#include "io.h"
+ Include dependency graph for pipeline.c:

Go to the source code of this file.

Data Structures

struct  fr_redis_command_s
 Represents a single command. More...
 
union  fr_redis_command_s.__unnamed62__
 
struct  fr_redis_command_s.__unnamed62__.__unnamed64__
 
struct  fr_redis_command_s.__unnamed62__.__unnamed66__
 
struct  fr_redis_command_set_s
 Represents a collection of pipelined commands. More...
 
struct  fr_redis_trunk_s
 

Macros

#define COMMAND_PRE_ALLOC_COUNT   8
 
#define COMMAND_PRE_ALLOC_LEN   64
 

Enumerations

enum  fr_redis_command_fmt_t {
  FR_REDIS_COMMAND_FMT_EXPANDED = 0 ,
  FR_REDIS_COMMAND_FMT_ARGV ,
  FR_REDIS_COMMAND_FMT_PREFORMATTED
}
 
enum  fr_redis_command_type_t {
  FR_REDIS_COMMAND_NORMAL = 0 ,
  FR_REDIS_COMMAND_TRANSACTION_START ,
  FR_REDIS_COMMAND_TRANSACTION_END
}
 

Functions

static int _command_set_free_list_free_on_exit (void *arg)
 Free any free requests when the thread is joined.
 
static int _redis_command_set_free (fr_redis_command_set_t *cmds)
 Free a command set.
 
static void _redis_pipeline_command_set_cancel (connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
 Deal with cancellation of sent requests.
 
static void _redis_pipeline_command_set_complete (UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED void *uctx)
 Signal the API client that we got a complete set of responses to a command set.
 
static void _redis_pipeline_command_set_fail (UNUSED request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
 Signal the API client that we failed enqueuing the commands.
 
static void _redis_pipeline_command_set_free (UNUSED request_t *request, void *preq, UNUSED void *uctx)
 Free the command set.
 
static connection_t * _redis_pipeline_connection_alloc (trunk_connection_t *tconn, fr_event_list_t *el, connection_conf_t const *conf, char const *log_prefix, void *uctx)
 
static void _redis_pipeline_demux (struct redisAsyncContext *ac, void *vreply, void *privdata)
 Callback for for receiving Redis replies.
 
static void _redis_pipeline_mux (UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
 Enqueue one or more command sets onto a redis handle.
 
static void _redis_trunk_active (UNUSED trunk_t *trunk, UNUSED trunk_state_t prev, UNUSED trunk_state_t state, void *uctx)
 
fr_redis_pipeline_status_t fr_redis_command_argv_add (fr_redis_command_set_t *cmds, size_t argc, char const **argv, size_t *argv_len, fr_redis_command_complete_t complete, void *rctx)
 Add a command with arguments to the command set.
 
char const * fr_redis_command_get_cmd (fr_redis_command_t *cmd)
 
fr_redis_pipeline_status_t fr_redis_command_literal_add (fr_redis_command_set_t *cmds, char const *cmd_str, fr_redis_command_complete_t complete, void *rctx)
 Add a literal command to the command set.
 
fr_redis_pipeline_status_t fr_redis_command_preformatted_add (fr_redis_command_set_t *cmds, char const *cmd_str, size_t cmd_len, fr_redis_command_complete_t complete, void *rctx)
 Add an preformatted command to the command set as formatted by redisCommandFormat or it's variants.
 
fr_redis_command_set_t * fr_redis_command_set_alloc (TALLOC_CTX *ctx, request_t *request, fr_redis_command_set_complete_t complete, fr_redis_command_set_fail_t fail, void *rctx, bool autofree)
 Allocate a new command set.
 
void fr_redis_command_set_cancel (fr_redis_command_set_t *cmds)
 Cancel a command set.
 
int fr_redis_command_set_clear (fr_redis_command_set_t *cmds)
 Reinitialise a command set so that it can be used again.
 
void fr_redis_command_set_next_node (fr_redis_command_set_t *cmds, fr_redis_io_conf_t *ioconf)
 Extract the next node address and port from a command set.
 
fr_redis_async_rcode_t fr_redis_command_set_rcode (fr_redis_command_set_t *cmds)
 Extract the rcode from a command set.
 
void fr_redis_command_set_rcode_set (fr_redis_command_set_t *cmds, fr_redis_async_rcode_t rcode)
 Set the rcode for a command set.
 
int fr_redis_command_set_reset (fr_redis_command_set_t *cmds)
 Reset a command set to it's state before enqueuing.
 
fr_redis_trunk_t * fr_redis_trunk_alloc (fr_redis_ct_t *rtcluster, fr_redis_io_conf_t const *io_conf, fr_pair_list_t *trigger_args, fr_redis_trunk_active_t active, void *active_uctx, bool active_oneshot)
 Allocate a new trunk.
 
static int redis_addr_from_redirect (TALLOC_CTX *ctx, char **addr, uint16_t *port, redisReply *redirect)
 Convert a MOVED / ASK reply into an address and port.
 
fr_redis_pipeline_status_t redis_command_set_enqueue (fr_redis_trunk_t *rtrunk, fr_redis_command_set_t *cmds)
 Enqueue a command set on a specific trunk.
 
static fr_redis_pipeline_status_t redis_command_transaction_check (request_t *request, fr_redis_command_type_t *type, fr_redis_command_set_t *cmds, char const *cmd)
 

Variables

static _Thread_local fr_dlist_head_t * command_set_free_list
 The thread local free list.
 

Detailed Description

Functions for pipelining commands.

Id
e0178ad7c432a6aeaa19c12bae65a4edc9952514
Author
Arran Cudbard-Bell (a.cud.nosp@m.bard.nosp@m.b@fre.nosp@m.erad.nosp@m.ius.o.nosp@m.rg)

Definition in file pipeline.c.


Data Structure Documentation

◆ fr_redis_command_s

struct fr_redis_command_s

Represents a single command.

Definition at line 62 of file pipeline.c.

+ Collaboration diagram for fr_redis_command_s:
Data Fields
union fr_redis_command_s.__unnamed62__ __unnamed__
fr_redis_command_set_t * cmds Command set this entry belongs to.
fr_redis_command_complete_t complete Callback to process result from this command.
fr_dlist_t entry Entry in the command buffer.
fr_redis_command_fmt_t fmt Redis command format.
void * rctx To be passed to the callback.
uint64_t sqn The sequence number of the command.

This is only valid for a specific handle, and is unique within the handle.

fr_redis_command_type_t type Redis command type.

◆ fr_redis_command_s.__unnamed62__

union fr_redis_command_s.__unnamed62__

Definition at line 69 of file pipeline.c.

Data Fields
struct fr_redis_command_s.__unnamed62__.__unnamed64__ __unnamed__
struct fr_redis_command_s.__unnamed62__.__unnamed66__ __unnamed__

◆ fr_redis_command_s.__unnamed62__.__unnamed64__

struct fr_redis_command_s.__unnamed62__.__unnamed64__

Definition at line 70 of file pipeline.c.

Data Fields
char const * str The command string.
size_t str_len Length of the command string.

◆ fr_redis_command_s.__unnamed62__.__unnamed66__

struct fr_redis_command_s.__unnamed62__.__unnamed66__

Definition at line 74 of file pipeline.c.

Data Fields
size_t argc Number of argv arguments.
char const ** argv Arguments for the redis command.
size_t * argv_len Lengths of the arguments.

◆ fr_redis_command_set_s

struct fr_redis_command_set_s

Represents a collection of pipelined commands.

Commands MUST map to the same cluster node if using clustering.

Definition at line 94 of file pipeline.c.

+ Collaboration diagram for fr_redis_command_set_s:
Data Fields
bool autofree Should the command set be freed when it is complete.
bool blocking This command set contains one or more commands which block the client (e.g.

WAIT)

fr_redis_command_set_complete_t complete Notify the creator of the command set that the command set has executed to to completion.

We have results for all commands.

fr_dlist_head_t completed Commands complete with replies.
fr_dlist_t entry
fr_redis_command_set_fail_t fail Notify the creator of the command set that the command set failed to execute to completion.

Partial results will be available.

uint32_t max_redirects How many times this command set may be redirected.

Copied from the cluster configuration when the command set is enqueued.

char * next_node_ip IP address of node from MOVED / ASK reply.
uint16_t next_node_port Port of node from MOVED / ASK reply.
fr_dlist_head_t pending Commands yet to be sent.
fr_redis_async_rcode_t rcode Code from last error returned.
void * rctx Resume context to write results to.
uint8_t redirected How many times this command set was redirected.
request_t * request Request this commands set is associated with (if any).
fr_dlist_head_t sent Commands sent.
trunk_request_t * treq Trunk request this command set is associated with.
uint16_t txn_end The number of times a transaction block ended in this command set.
uint16_t txn_start Number of times a transaction block was started in this command set.
bool txn_watch Transaction was started with a watch statement.

◆ fr_redis_trunk_s

struct fr_redis_trunk_s

Definition at line 163 of file pipeline.c.

+ Collaboration diagram for fr_redis_trunk_s:
Data Fields
fr_redis_trunk_active_t active Callback to run when the trunk becomes active.
void * active_uctx Uctx to pass to active callback.
fr_redis_io_conf_t const * io_conf Redis I/O configuration.

Specifies how to connect to the host this trunk is used to communicate with.

fr_redis_ct_t * rtcluster Cluster this trunk belongs to.
trunk_t * trunk Trunk containing all the connections to a specific host.

Macro Definition Documentation

◆ COMMAND_PRE_ALLOC_COUNT

#define COMMAND_PRE_ALLOC_COUNT   8

◆ COMMAND_PRE_ALLOC_LEN

#define COMMAND_PRE_ALLOC_LEN   64

Enumeration Type Documentation

◆ fr_redis_command_fmt_t

Enumerator
FR_REDIS_COMMAND_FMT_EXPANDED 

A command as a single string.

FR_REDIS_COMMAND_FMT_ARGV 

A command as an argv array.

FR_REDIS_COMMAND_FMT_PREFORMATTED 

A command preformatted with redisCommandFormat.

Definition at line 53 of file pipeline.c.

◆ fr_redis_command_type_t

Enumerator
FR_REDIS_COMMAND_NORMAL 

A normal, non-transactional command.

FR_REDIS_COMMAND_TRANSACTION_START 

Start of a transaction block.

Either WATCH or MULTI. if a transaction is started with WATCH, then multi is not marked up as a transaction start.

FR_REDIS_COMMAND_TRANSACTION_END 

End of a transaction block.

Either EXEC or DISCARD. If this command fails with MOVED or ASK, all commands back to the previous MULTI command must be requeued.

Definition at line 42 of file pipeline.c.

Function Documentation

◆ _command_set_free_list_free_on_exit()

static int _command_set_free_list_free_on_exit ( void *  arg)
static

Free any free requests when the thread is joined.

Definition at line 177 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_command_set_free()

static int _redis_command_set_free ( fr_redis_command_set_t *  cmds)
static

Free a command set.

Definition at line 192 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_command_set_cancel()

static void _redis_pipeline_command_set_cancel ( connection_t *  conn,
void *  preq,
trunk_cancel_reason_t  reason,
UNUSED void *  uctx 
)
static

Deal with cancellation of sent requests.

We can't actually signal redis to not process the request, so depending on why the commands were cancelled, we either tell the handle to ignore them, or move them back into the pending list.

Definition at line 853 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_command_set_complete()

static void _redis_pipeline_command_set_complete ( UNUSED request_t *  request,
void *  preq,
UNUSED void *  rctx,
UNUSED void *  uctx 
)
static

Signal the API client that we got a complete set of responses to a command set.

Definition at line 916 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_command_set_fail()

static void _redis_pipeline_command_set_fail ( UNUSED request_t *  request,
void *  preq,
UNUSED void *  rctx,
UNUSED trunk_request_state_t  state,
UNUSED void *  uctx 
)
static

Signal the API client that we failed enqueuing the commands.

Definition at line 928 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_command_set_free()

static void _redis_pipeline_command_set_free ( UNUSED request_t *  request,
void *  preq,
UNUSED void *  uctx 
)
static

Free the command set.

Definition at line 941 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_connection_alloc()

static connection_t * _redis_pipeline_connection_alloc ( trunk_connection_t *  tconn,
fr_event_list_t *  el,
connection_conf_t const *  conf,
char const *  log_prefix,
void *  uctx 
)
static

Definition at line 756 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_demux()

static void _redis_pipeline_demux ( struct redisAsyncContext *  ac,
void *  vreply,
void *  privdata 
)
static

Callback for for receiving Redis replies.

This is called by hiredis for each response is receives. privData is set to the fr_command_set

Note
Called only from hiredis, not the trunk itself.
Parameters
[in]acThe async context the command was enqueued on.
[in]vreplyredisReply containing the result of the command.
[in]privdatafr_redis_command_t that was sent to the Redis server. The fr_redis_command_t contains a pointer to the fr_redis_command_set_t which holds the treq which we use to signal that we have responses for all commands.

Definition at line 635 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_pipeline_mux()

static void _redis_pipeline_mux ( UNUSED fr_event_list_t *  el,
trunk_connection_t *  tconn,
connection_t *  conn,
UNUSED void *  uctx 
)
static

Enqueue one or more command sets onto a redis handle.

Because the trunk is in always writable mode, _redis_pipeline_mux will be called any time trunk_request_enqueue is called, so there'll only ever be one command to dequeue.

Parameters
[in]elEvent list for trunk events. Unused.
[in]tconnTrunk connection holding the commands to enqueue.
[in]connConnection handle containing the fr_redis_handle_t.
[in]uctxfr_redis_cluster_t. Unused.

Definition at line 781 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ _redis_trunk_active()

static void _redis_trunk_active ( UNUSED trunk_t *  trunk,
UNUSED trunk_state_t  prev,
UNUSED trunk_state_t  state,
void *  uctx 
)
static

Definition at line 972 of file pipeline.c.

+ Here is the caller graph for this function:

◆ fr_redis_command_argv_add()

fr_redis_pipeline_status_t fr_redis_command_argv_add ( fr_redis_command_set_t *  cmds,
size_t  argc,
char const **  argv,
size_t *  argv_len,
fr_redis_command_complete_t  complete,
void *  rctx 
)

Add a command with arguments to the command set.

The command and arguments must either be entirely static, or parented by the command set.

Parameters
[in]cmdsCommand set to add command to.
[in]argcNumber of arguments.
[in]argvRedis command arguments.
[in]argv_lenLength of the command arguments.
[in]completeCallback to run when this command completes
[in]rctxto pass to complete
Returns
  • FR_REDIS_PIPELINE_BAD_CMDS if a bad command sequence is enqueued.
  • FR_REDIS_PIPELINE_OK if command was enqueued successfully.

Definition at line 439 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_command_get_cmd()

char const * fr_redis_command_get_cmd ( fr_redis_command_t *  cmd)

Definition at line 1025 of file pipeline.c.

+ Here is the caller graph for this function:

◆ fr_redis_command_literal_add()

fr_redis_pipeline_status_t fr_redis_command_literal_add ( fr_redis_command_set_t *  cmds,
char const *  cmd_str,
fr_redis_command_complete_t  complete,
void *  rctx 
)

Add a literal command to the command set.

The command must either be entirely static, or parented by the command set.

Note
Caller should disallow "SUBSCRIBE" et al, if they're not appropriate. As subscribing to a stream where we're not expecting it would break things, badly.
Parameters
[in]cmdsCommand set to add command to.
[in]cmd_strA fully expanded/formatted command to send to redis. Must be static, or have the same lifetime as the command set (allocated with the command set as the parent).
[in]completeCallback to run when this command completes
[in]rctxto pass to complete
Returns
  • FR_REDIS_PIPELINE_BAD_CMDS if a bad command sequence is enqueued.
  • FR_REDIS_PIPELINE_OK if command was enqueued successfully.

Definition at line 404 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_command_preformatted_add()

fr_redis_pipeline_status_t fr_redis_command_preformatted_add ( fr_redis_command_set_t *  cmds,
char const *  cmd_str,
size_t  cmd_len,
fr_redis_command_complete_t  complete,
void *  rctx 
)

Add an preformatted command to the command set as formatted by redisCommandFormat or it's variants.

The command must either be entirely static, or parented by the command set.

Note
Caller should disallow "SUBSCRIBE" et al, if they're not appropriate. As subscribing to a stream where we're not expecting it would break things, badly.
Parameters
[in]cmdsCommand set to add command to.
[in]cmd_strA fully formatted command to send to redis. Must be static, or have the same lifetime as the command set (allocated with the command set as the parent).
[in]cmd_lenThe length of cmd_str (as returned by redisCommandForamt)
[in]completeCallback to run when this command completes
[in]rctxto pass to complete
Returns
  • FR_REDIS_PIPELINE_BAD_CMDS if a bad command sequence is enqueued.
  • FR_REDIS_PIPELINE_OK if command was enqueued successfully.

Definition at line 482 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_command_set_alloc()

fr_redis_command_set_t * fr_redis_command_set_alloc ( TALLOC_CTX *  ctx,
request_t *  request,
fr_redis_command_set_complete_t  complete,
fr_redis_command_set_fail_t  fail,
void *  rctx,
bool  autofree 
)

Allocate a new command set.

This is a set of commands that the calling module wants to execute on the redis server in sequence.

Control will be returned to the caller via the registered complete and fail functions.

Parameters
[in]ctxto bind the command set's lifetime to.
[in]requestto pass to places that need it.
[in]completeFunction to call when all commands have been processed.
[in]failFunction to call if the command set was not executed or was partially executed.
[in]rctxResume context to pass to complete and fail functions.
[in]autofreeShould the command set be freed when completed.
Returns
A new or refurbished command set.

Definition at line 252 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_command_set_cancel()

void fr_redis_command_set_cancel ( fr_redis_command_set_t *  cmds)

Cancel a command set.

Parameters
[in]cmdsCommand set to cancel.

Definition at line 566 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_command_set_clear()

int fr_redis_command_set_clear ( fr_redis_command_set_t *  cmds)

Reinitialise a command set so that it can be used again.

Frees the completed commands, and returns the command set to the state it was in when it was created via fr_redis_command_set_alloc(). The command set keeps the request, the resume ctx, the callbacks, and the autofree setting it was allocated with, so that the caller can add a new set of commands and enqueue the command set again.

Note
The caller must have finished reading the results before calling this function. The completed commands are freed here, as is the address of the node named by a MOVED / ASK reply. fr_redis_command_set_rcode() reports success after this function returns, whatever the previous execution did.
Parameters
[in]cmdsCommand set to reinitialise.
Returns
  • 0 on success.
  • -1 if the command set is still executing, in which case nothing is changed.

Definition at line 1106 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_command_set_next_node()

void fr_redis_command_set_next_node ( fr_redis_command_set_t *  cmds,
fr_redis_io_conf_t *  ioconf 
)

Extract the next node address and port from a command set.

Definition at line 1054 of file pipeline.c.

+ Here is the caller graph for this function:

◆ fr_redis_command_set_rcode()

fr_redis_async_rcode_t fr_redis_command_set_rcode ( fr_redis_command_set_t *  cmds)

Extract the rcode from a command set.

Definition at line 1040 of file pipeline.c.

+ Here is the caller graph for this function:

◆ fr_redis_command_set_rcode_set()

void fr_redis_command_set_rcode_set ( fr_redis_command_set_t *  cmds,
fr_redis_async_rcode_t  rcode 
)

Set the rcode for a command set.

Definition at line 1047 of file pipeline.c.

+ Here is the caller graph for this function:

◆ fr_redis_command_set_reset()

int fr_redis_command_set_reset ( fr_redis_command_set_t *  cmds)

Reset a command set to it's state before enqueuing.

For use when handling MOVED / ASK where the command set needs to be sent to another node.

Definition at line 1065 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fr_redis_trunk_alloc()

fr_redis_trunk_t * fr_redis_trunk_alloc ( fr_redis_ct_t *  rtcluster,
fr_redis_io_conf_t const *  io_conf,
fr_pair_list_t *  trigger_args,
fr_redis_trunk_active_t  active,
void *  active_uctx,
bool  active_oneshot 
)

Allocate a new trunk.

Parameters
[in]rtclusterto allocate the trunk for.
[in]io_confDescribing the connection to a single REDIS host.
[in]trigger_argsPairs to pass to trigger requests, if triggers are enabled.
[in]activeCallback to run when the trunk becomes active.
[in]active_uctxUctx to pass to active callback.
[in]active_oneshotShould the call back be run just once.
Returns
  • On success, a new fr_redis_trunk_t which can be used for pipelining commands.
  • NULL on failure.

Definition at line 991 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ redis_addr_from_redirect()

static int redis_addr_from_redirect ( TALLOC_CTX *  ctx,
char **  addr,
uint16_t *  port,
redisReply *  redirect 
)
static

Convert a MOVED / ASK reply into an address and port.

Definition at line 578 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ redis_command_set_enqueue()

fr_redis_pipeline_status_t redis_command_set_enqueue ( fr_redis_trunk_t *  rtrunk,
fr_redis_command_set_t *  cmds 
)

Enqueue a command set on a specific trunk.

The command set may be passed around several trunks before it is complete. This is to allow it to follow MOVED and ASK responses.

Parameters
[in]rtrunkto enqueue command set on.
[in]cmdsCommand set to enqueue.
Returns
  • FR_REDIS_PIPELINE_OK if commands were immediately enqueued or placed in the backlog.
  • FR_REDIS_PIPELINE_DST_UNAVAILABLE if the REDIS host is unreachable.
  • FR_REDIS_PIPELINE_FAIL any other general error.

Definition at line 535 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ redis_command_transaction_check()

static fr_redis_pipeline_status_t redis_command_transaction_check ( request_t *  request,
fr_redis_command_type_t *  type,
fr_redis_command_set_t *  cmds,
char const *  cmd 
)
static

Definition at line 304 of file pipeline.c.

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ command_set_free_list

_Thread_local fr_dlist_head_t* command_set_free_list
static

The thread local free list.

Any entries remaining in the list will be freed when the thread is joined

Definition at line 40 of file pipeline.c.