Static variables

38 views
Skip to first unread message

edward

unread,
Dec 17, 2009, 11:46:11 AM12/17/09
to Relacy Race Detector
Hi,

What is the recommended usage of static variables with Relacy?

For example, I have some simplified code like this:
http://relacy.pastebin.com/m48ce35bd

The problem is that because the static variable theMutexOwner is
constructed outside of rl::simulate(), I get a crash. I can't think of
a clean way of ensuring that my static variable is only initialized
within rl::simulate(). For now, I've added hacky workarounds by
manually re-instancing the static variable every time in before()
using intrinsic knowledge of Relacy. Any ideas?

Thanks,
-Edward

Dmitriy Vyukov

unread,
Dec 20, 2009, 7:27:52 AM12/20/09
to Relacy Race Detector


Hi Edward,
Sorry for delay.

Well, initially Relacy was not intended for verification of programs
with static/thread local data. The idea was that a user may just
declare a variable in test class to simulate static/global variable,
or declare an array in test class to simulate thread local variable.

You may use a function rl::thread_index() to get a thread index (value
passed into test::thread(unsigned) function), thread index is a value
in 0..thread_count-1.

Btw, does theMutexOwner variable in your test must be static? AFAIU,
it may be plain array of variables. I.e.:


class AbortableLock
{
public:
// ....
private:
MUTEX myMutex;
std::atomic<int> theMutexOwner [MAX_THREAD_COUNT];

std::atomic<int>& get()
{
return theMutexOwner[rl::thread_index()];
}
};


--
Dmitriy V'jukov

Dmitriy Vyukov

unread,
Dec 20, 2009, 8:30:41 AM12/20/09
to Relacy Race Detector
On Dec 20, 3:27 pm, Dmitriy Vyukov <dvyu...@gmail.com> wrote:
> On Dec 17, 7:46 pm, edward <e4...@yahoo.com> wrote:

> > What is the recommended usage of static variables with Relacy?
>
> > For example, I have some simplified code like this:
> >    http://relacy.pastebin.com/m48ce35bd
>
> > The problem is that because the static variable theMutexOwner is
> > constructed outside of rl::simulate(), I get a crash. I can't think of
> > a clean way of ensuring that my static variable is only initialized
> > within rl::simulate(). For now, I've added hacky workarounds by
> > manually re-instancing the static variable every time in before()
> > using intrinsic knowledge of Relacy. Any ideas?
>
> Hi Edward,
> Sorry for delay.
>
> Well, initially Relacy was not intended for verification of programs
> with static/thread local data. The idea was that a user may just
> declare a variable in test class to simulate static/global variable,
> or declare an array in test class to simulate thread local variable.


Relacy 2.0 includes TLS variable simulation, it may be just what you
need.
Declare a var as TLS_T(T), it may be automatic var, class member,
static class member or global variable. Then use TLS(v) for accesses:

TLS_T(unsigned) tls_global_test_x;

struct tls_global_test : rl::test_suite<tls_global_test, 3,
rl::test_result_user_assert_failed>
{
void thread(unsigned index)
{
RL_ASSERT(VAR(tls_global_test_x) == 0);
VAR(tls_global_test_x) = index + 10;
RL_ASSERT(VAR(tls_global_test_x) == index + 10);
RL_ASSERT(false);
}
};

TLS variables are reset to 0 before each iteration.


--
Dmitriy V'jukov

edward

unread,
Dec 22, 2009, 9:17:50 AM12/22/09
to Relacy Race Detector
On Dec 20, 8:30 am, Dmitriy Vyukov <dvyu...@gmail.com> wrote:
> Relacy 2.0 includes TLS variable simulation, it may be just what you
> need.

I also require random access to the TLS variable values. eg. given a
thread id, return the TLS value of that thread. Do you have any
suggestions for what sort of TLS API I could emulate to augment Relacy
with this ability?

I actually misnamed "theMutexOwner" in my attempt to simplify the code
for posting. Allow me to give some more background. I have an
AbortableLock is a class that performs deadlock detection amongst all
instances of the class. It does so by maintaining two pieces of
information:
- For each instance of AbortableLock, it keeps track of the thread
id that has currently acquired it
- Inside AbortableLock::lock(), it saves into a TLS variable that
pointer of the AbortableLock instance that is currently trying to
acquire the mutex. This gives us a map from the thread id to the lock
that is trying to acquire (or "wait on") the mutex. Once it is done
trying to acquire the mutex, it resets the TLS variable to NULL.

Hopefully my previous questions now make sense. :) Inside
AbortableLock::lock(), we do a timed lock on the underlying mutex.
When it times out, we attempt to perform deadlock detection. This
consists of walking the "wait graph" by successively looking up these
two pieces of information like so:
- From the current AbortableLock instance, obtain its mutex owner
thread id
- From the owner thread id, use the TLS variable to find which
AbortableLock instance it is currently waiting on
- Now we have a new AbortableLock instance that we repeat the above
two steps with. If we find a repeated thread id, then a deadlock is
assumed.

Thanks,
-Edward

Dmitriy Vyukov

unread,
Jan 10, 2010, 8:14:47 AM1/10/10
to Relacy Race Detector


Hi Edward,

As far as I see you just need global variable where you will put array
of per-thread data. Right?

Since the array itself is actually constant for a duration of an
execution, you may simulate this in the following way. The trick is
just to introduce another level of indirection.

std::atomic<int>* g_tls;

struct test : rl::test_suite<test, THREAD_COUNT>
{
void before()
{
g_tls = new std::atomic<int> [THREAD_COUNT];
for (size_t i = 0; i != THREAD_COUNT; i += 1)
g_tls[i].store(0, std::memory_order_relaxed);
}

void after()
{
delete [] g_tls;
}
};

void some_freestanding_func()
{
std::atomic<int>& my_slot = g_tls[rl::thread_index()];
...
}


--
Dmitriy V'jukov

edward

unread,
Jan 10, 2010, 4:27:19 PM1/10/10
to Relacy Race Detector
Hi Dmitriy,

On Jan 10, 8:14 am, Dmitriy Vyukov <dvyu...@gmail.com> wrote:

> As far as I see you just need global variable where you will put array
> of per-thread data. Right?
>
> Since the array itself is actually constant for a duration of an
> execution, you may simulate this in the following way. The trick is
> just to introduce another level of indirection.
>
> std::atomic<int>* g_tls;

Yes, that is what I've been doing, which I originally referred to as a
"hacky workaround". The question is how does one go about doing it
cleanly in C++ so that the synchronization class code knows nothing
about Relacy yet while being able to easily test with Relacy when
needed. When not using Relacy, your proposal still requires that extra
level of indirection, plus it needs some way to know that we're not
compiling for Relacy so that it can initialize g_tls properly.

-Edward

Dmitriy Vyukov

unread,
Feb 9, 2010, 2:44:38 AM2/9/10
to Relacy Race Detector

Hi Edward,

That makes sense. I will think about support for global atomic/var
variables.

Initially, RRD was intended for small synthetic tests only. I assumed
that people will code an implementation specifically for RDD (so such
things was not a problem). Driven by my experience and user feedback,
RRD develop toward verification of production code more and more.
First it was reduction in manual instrumentation, then tls, then
dynamic threads...
I have limited experience with checking of a full-fledged work-
dispatching system, I was able to "boot" the scheduler, dispatch some
work, then "shutdown" the scheduler, all under Relacy. However, the
work-dispatching library uses only thread-local global variables
(__declspec(thread)), I especially tried to avoid using global vars as
much as possible, that's why currently Relacy has support only for
global thread-local vars, and not atomic/var...

--
Dmitriy V'jukov

Reply all
Reply to author
Forward
0 new messages