Skip to content

Add pthread_barrier event #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions doc/tutorial.txt
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,54 @@ generates the following sequence:
"unlock" : "SyncPointA" (internal mutex)
}

* pthread_barrier: String. Used exactly like barrier, however this actually uses
pthread_barrier_wait. As discussed above, this is likely to cause problems with
killing rt-app.

Why would you want this instead of 'barrier'? 'barrier' uses pthread_cond_wait
and pthread_cond_broadcast. Note that pthread_cond_wait is passed a mutex, and
that this mutex must be held by the caller. pthread_cond_wait releases this
mutex, and must re-acquire it before returning. That means that when multiple
waiters are woken by a pthread_cond_broadcast, all but one of them would have to
immediately go back to sleep, waiting on the mutex, before returning from
pthread_cond_wait. Therefore pthread implementations use a different futex
mechanism for pthread_cond_wait than for pthread_barrier_wait: instead of using
FUTEX_WAKE to wake all the waiters, FUTEX_CMP_REQUEUE is used to wake _one_of
the waiters, and _move_ the remaining waiters so that instead of waiting on the
condition variable itself, they are now waiting on the mutex. When the first
waiter is woken it will release the mutex before returning from
pthread_cond_wait, which will wake the second waiter. When the second waiter
releases the mutex, it will release the third waiter, and so on. This can impact
scheduler behaviour - instead of waking up multiple threads from a single
context we now wake one thread from each context:

Using 'pthread_barrier' event:


Thread A | Thread B | Thread C
=========================================================================================
| | pthread_barrier_wait(&b)
| pthread_barrier_wait(&b) |
pthread_barrier_wait(&b) | |
\--WAKES-->| # unblocked by A # |
\--WAKES--------------------------------->| # unblocked by A #


Using 'barrier' event:


Thread A | Thread B | Thread C
=========================================================================================
| | pthread_cond_wait(&c, &m)
| pthread_cond_wait(&c, &m) |
pthread_cond_broadcast(&c) | |
\--WAKES-->| # unblocked by A # |
| \--WAKES-->| # unblocked by B #


Therefore, you might want to use the pthread_barrier event even though it can
cause problems with killing rt-app.

* suspend : String. Block the calling thread until another thread wakes it up
with resume. The String can be let empty as it will be filled by workgen with
the right thread's name before starting the use case.
Expand Down
4 changes: 4 additions & 0 deletions src/rt-app.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,10 @@ static int run_event(event_data_t *event, int dry_run,
}
pthread_mutex_unlock(&(rdata->res.barrier.m_obj));
break;
case rtapp_pthread_barrier:
log_debug("pthread_barrier %s", rdata->name);
pthread_barrier_wait(&rdata->res.pthread_barrier.obj);
break;
case rtapp_sig_and_wait:
log_debug("signal and wait %s", rdata->name);
pthread_cond_signal(&(rdata->res.cond.obj));
Expand Down
54 changes: 48 additions & 6 deletions src/rt-app_parse_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ static void init_barrier_resource(rtapp_resource_t *data, const rtapp_options_t
pthread_cond_init(&data->res.barrier.c_obj, NULL);
}

static void finalize_pthread_barrier_resource(rtapp_resource_t *data, const rtapp_options_t *opts)
{
int err;
int thread_count = data->res.pthread_barrier.thread_ref_count;

log_info(PIN3 "Finalize: %s pthread_barrier %d threads", data->name, thread_count);

err = pthread_barrier_init(&data->res.pthread_barrier.obj, NULL, thread_count);
if (err) {
perror("pthread_barrier_init");
}
}

static void
init_resource_data(const char *name, int type, int idx, const rtapp_options_t *opts)
{
Expand Down Expand Up @@ -267,6 +280,9 @@ init_resource_data(const char *name, int type, int idx, const rtapp_options_t *o
case rtapp_barrier:
init_barrier_resource(data, opts);
break;
case rtapp_pthread_barrier:
/* Init all done in finalize_resources */
break;
default:
break;
}
Expand Down Expand Up @@ -357,6 +373,21 @@ static int get_resource_index(const char *name, int type, rtapp_options_t *opts)
return i;
}

static int finalize_resources(rtapp_options_t *opts)
{
int i;

for (i = 0; i < opts->nresources; i++) {
switch (opts->resources[i].type) {
case rtapp_pthread_barrier:
finalize_pthread_barrier_resource(&opts->resources[i], opts);
break;
default:
break;
}
}
}

static char* create_unique_name(char *tmp, int size, const char* ref, long tag)
{
snprintf(tmp, size, "%s%lx", ref, (long)(tag));
Expand Down Expand Up @@ -499,21 +530,30 @@ parse_thread_event_data(char *name, struct json_object *obj,
return;
}

if (!strncmp(name, "barrier", strlen("barrier"))) {
if (!strncmp(name, "barrier", strlen("barrier")) ||
!strncmp(name, "pthread_barrier", strlen("pthread_barrier"))) {
int thread_count;

if (!json_object_is_type(obj, json_type_string))
goto unknown_event;

data->type = rtapp_barrier;
if (!strncmp(name, "barrier", strlen("barrier")))
data->type = rtapp_barrier;
else
data->type = rtapp_pthread_barrier;

ref = json_object_get_string(obj);
i = get_resource_index(ref, rtapp_barrier, opts);
i = get_resource_index(ref, data->type, opts);

data->res = i;
rdata = &(opts->resources[data->res]);
rdata->res.barrier.waiting += 1;
if (data->type == rtapp_barrier)
thread_count = ++rdata->res.barrier.waiting;
else
thread_count = ++rdata->res.pthread_barrier.thread_ref_count;

log_info(PIN2 "type %d target %s [%d] %d users so far", data->type, rdata->name, rdata->index, rdata->res.barrier.waiting);
log_info(PIN2 "type %d target %s [%d] %d users so far",
data->type, rdata->name, rdata->index, thread_count);
return;
}

Expand Down Expand Up @@ -631,6 +671,7 @@ static char *events[] = {
"iorun",
"yield",
"barrier",
"pthread_barrier",
NULL
};

Expand Down Expand Up @@ -1051,7 +1092,8 @@ get_opts_from_json_object(struct json_object *root, rtapp_options_t *opts)
parse_tasks(tasks, opts);
json_object_put(tasks);
log_info(PFX "Free json objects");

log_info(PFX "Finalize resources");
finalize_resources(opts);
}

void
Expand Down
10 changes: 9 additions & 1 deletion src/rt-app_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ typedef enum resource_t
rtapp_iorun,
rtapp_runtime,
rtapp_yield,
rtapp_barrier
rtapp_barrier,
rtapp_pthread_barrier
} resource_t;

struct _rtapp_mutex {
Expand All @@ -99,6 +100,12 @@ struct _rtapp_barrier_like {
pthread_cond_t c_obj;
};

struct _rtapp_pthread_barrier {
pthread_barrier_t obj;
/* Number of threads that refer to this barrier */
int thread_ref_count;
};

struct _rtapp_signal {
pthread_cond_t *target;
};
Expand Down Expand Up @@ -128,6 +135,7 @@ typedef struct _rtapp_resource_t {
struct _rtapp_iomem_buf buf;
struct _rtapp_iodev dev;
struct _rtapp_barrier_like barrier;
struct _rtapp_pthread_barrier pthread_barrier;
} res;
int index;
resource_t type;
Expand Down