470,870 Members | 1,406 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 470,870 developers. It's quick & easy.

Reentrant code

I am modifying some legacy code, to make the functions reentrant. I use
lots of global data (large nested structs) to pass data around quicky.
The global structures are far too large (in size) for each of my
functions to maintain copies.

Any suggestions on the best way to write a reentrant version of my
library? - bearing in mind that my global vars are mostly, pointers to
*HUGE* structures.
Jun 27 '08 #1
12 3265
On Jun 17, 12:10 pm, Bit Byte <r...@yourbox.comwrote:
I am modifying some legacy code, to make the functions reentrant. I use
lots of global data (large nested structs) to pass data around quicky.
The global structures are far too large (in size) for each of my
functions to maintain copies.

Any suggestions on the best way to write a reentrant version of my
library? - bearing in mind that my global vars are mostly, pointers to
*HUGE* structures.

Your question is off-topic as C does not say anything about multi-
threading.

<off-topic>
That greatly depends on the function you are trying to make re-
entrant. A general approach is to use some sort of mutex to prevent
the global data from being altered in two place in the same places
leaving the result in an inconsistent state.
<just-curious>
You say your code uses a lot of globals. I think that's a bad idea
and may be you should do a design review.
</just-curious>
One thing to mention here is mutex uses co-operative locking. To
elaborate, mutex is a door through which only one person can enter at
a time and threads are standing in a queue. If the threads are
maintaining the queue, everything is great but the door does not stop
you from jumping out of the window.

Its not the running code that is to be made re-entrant. The access to
the shared data is to be synchronized. Lock the mutex before
modification and unlock it when you are done. Depending on your
problem, you may even have to use condition variables.
</off-topic>
Jun 27 '08 #2
On Jun 17, 11:33 am, rahul <rahulsin...@gmail.comwrote:
On Jun 17, 12:10 pm, Bit Byte <r...@yourbox.comwrote:
I am modifying some legacy code, to make the functions reentrant. I use
lots of global data (large nested structs) to pass data around quicky.
The global structures are far too large (in size) for each of my
functions to maintain copies.
Any suggestions on the best way to write a reentrant version of my
library? - bearing in mind that my global vars are mostly, pointers to
*HUGE* structures.

Your question is off-topic as C does not say anything about multi-
threading.
Re-entrancy doesn't have to do anything with threads or thread safety,
but indeed ISO C doesn't mention thing such as 'reentrancy', 'threads'
etc.
"Bit Byte", if you are programming for windows, try a windows
newsgroup. If you are programming for unix, try comp.unix.programmer.
<snip rahul's off-topic reply>
Jun 27 '08 #3
vi******@gmail.com writes:
[...]
Re-entrancy doesn't have to do anything with threads or thread safety,
but indeed ISO C doesn't mention thing such as 'reentrancy', 'threads'
etc.
[...]

It does, but only briefly.

C99 7.1.4p4:

The functions in the standard library are not guaranteed to be
reentrant and may modify objects with static storage duration.

footnote:

Thus, a signal handler cannot, in general, call standard library
functions.

There's also an index entry for "reentrancy", pointing to 5.1.2.3
(Program execution) and 5.2.3 (Signals an interrupts) as well as
7.1.4.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jun 27 '08 #4
Bit Byte wrote:
I am modifying some legacy code, to make the functions reentrant. I use
lots of global data (large nested structs) to pass data around quicky.
The global structures are far too large (in size) for each of my
functions to maintain copies.

Any suggestions on the best way to write a reentrant version of my
library? - bearing in mind that my global vars are mostly, pointers to
*HUGE* structures.
You will have to protect your global data structures so that
only a single thread access them at any given moment.

There are no "miracle" solutions, sorry.

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32
Jun 27 '08 #5
Bit Byte wrote:
I am modifying some legacy code, to make the functions reentrant. I use
lots of global data (large nested structs) to pass data around quicky.
The global structures are far too large (in size) for each of my
functions to maintain copies.

Any suggestions on the best way to write a reentrant version of my
library? - bearing in mind that my global vars are mostly, pointers to
*HUGE* structures.
Others have said there's no re-entrancy in C, but I don't
understand why they think that. A C function running in the
"main line" of the program might also be called from a handler
for an asynchronous signal, and if you want to do this you'll
need to be sure the function is re-entrant. And you can make
it so, with due diligence.

Aside from that limited situation, though, C has no notion
of "concurrent execution," nothing like multiple threads or
parallel processes or anything of that kind. If your problem
involves such beyond-C things then mere re-entrancy alone may
not be enough; you should probably seek advice on a forum like
comp.programming.threads.

But to the mechanics: Eliminating the global variables is
not in and of itself enough to make a function re-entrant, but
it's usually a necessary first step. If you've got a global
and a function that manipulates it:

int global;
void set_global(int new_value) {
global = new_value;
}

.... you can get rid of the global by giving the function an
extra argument that points to a non-global substitute:

void set_global(int *global, int new_value) {
*global = new_value;
}

This can become tiresome if there are a lot of globals to
be eliminated, and/or if the functions call each other a lot:
You'll get snarled passing a lot of pointer arguments all over
the place and probably getting some of them wrong. But much
of that can be smoothed over by using a struct:

/* Old */
int global1;
double global2;
char *global3;
void func(void) {
global1 = 42;
global2 = 42.0;
other_func();
}
void other_func(void) {
global3 = "XLII";
}

/* New */
struct globals {
int global1;
double global2;
char *global3;
};
void func(struct globals *gp) {
gp->global1 = 42;
gp->global2 = 42.0;
other_func(gp);
}
void other_func(struct globals *gp) {
gp->global3 = "XLII";
}

If there's a lot of this you may be tempted to use macros
to ease the transition:

struct globals { ... as above ... };
#define global1 gp->global1
#define global2 gp->global2
#define global3 gp->global3

.... which allows you to leave the existing functions almost
unchanged: The source still refers to plain old global2 and
so on, with the pointer automagically inserted. Do this if
you really, really feel you must -- but I've been down that
road, and just a few bends beyond where you can see it gets
rocky and rutted. Better to do the scut-work up front than
to maintain a hack for ever and ever.

--
Eric Sosman
es*****@ieee-dot-org.invalid
Jun 27 '08 #6
Eric Sosman wrote:
Bit Byte wrote:
>I am modifying some legacy code, to make the functions reentrant. I
use lots of global data (large nested structs) to pass data around
quicky. The global structures are far too large (in size) for each of
my functions to maintain copies.

Any suggestions on the best way to write a reentrant version of my
library? - bearing in mind that my global vars are mostly, pointers to
*HUGE* structures.

Others have said there's no re-entrancy in C, but I don't
understand why they think that. A C function running in the
"main line" of the program might also be called from a handler
for an asynchronous signal, and if you want to do this you'll
need to be sure the function is re-entrant. And you can make
it so, with due diligence.

Aside from that limited situation, though, C has no notion
of "concurrent execution," nothing like multiple threads or
parallel processes or anything of that kind. If your problem
involves such beyond-C things then mere re-entrancy alone may
not be enough; you should probably seek advice on a forum like
comp.programming.threads.

But to the mechanics: Eliminating the global variables is
not in and of itself enough to make a function re-entrant, but
it's usually a necessary first step. If you've got a global
and a function that manipulates it:

int global;
void set_global(int new_value) {
global = new_value;
}

... you can get rid of the global by giving the function an
extra argument that points to a non-global substitute:

void set_global(int *global, int new_value) {
*global = new_value;
}

This can become tiresome if there are a lot of globals to
be eliminated, and/or if the functions call each other a lot:
You'll get snarled passing a lot of pointer arguments all over
the place and probably getting some of them wrong. But much
of that can be smoothed over by using a struct:

/* Old */
int global1;
double global2;
char *global3;
void func(void) {
global1 = 42;
global2 = 42.0;
other_func();
}
void other_func(void) {
global3 = "XLII";
}

/* New */
struct globals {
int global1;
double global2;
char *global3;
};
void func(struct globals *gp) {
gp->global1 = 42;
gp->global2 = 42.0;
other_func(gp);
}
void other_func(struct globals *gp) {
gp->global3 = "XLII";
}

If there's a lot of this you may be tempted to use macros
to ease the transition:

struct globals { ... as above ... };
#define global1 gp->global1
#define global2 gp->global2
#define global3 gp->global3

... which allows you to leave the existing functions almost
unchanged: The source still refers to plain old global2 and
so on, with the pointer automagically inserted. Do this if
you really, really feel you must -- but I've been down that
road, and just a few bends beyond where you can see it gets
rocky and rutted. Better to do the scut-work up front than
to maintain a hack for ever and ever.
Good advice indeed. Only bit I don't understand is your last statement:
"Better to do the scut-work up front than to maintain a hack for ever
and ever."

I thought that 'wrapping" the globals in a struct and using macros to
access fields (posibly with mutex/semaphores) was a good idea - but
after you last statement, I am not sure if you mean it is a 'hack' -
since you have been down this route already, your views are of interest
to me ...
Jun 27 '08 #7
Bit Byte wrote:
Eric Sosman wrote:
>[...]
If there's a lot of this you may be tempted to use macros
to ease the transition:

struct globals { ... as above ... };
#define global1 gp->global1
#define global2 gp->global2
#define global3 gp->global3

... which allows you to leave the existing functions almost
unchanged: The source still refers to plain old global2 and
so on, with the pointer automagically inserted. Do this if
you really, really feel you must -- but I've been down that
road, and just a few bends beyond where you can see it gets
rocky and rutted. Better to do the scut-work up front than
to maintain a hack for ever and ever.

Good advice indeed. Only bit I don't understand is your last statement:
"Better to do the scut-work up front than to maintain a hack for ever
and ever."

I thought that 'wrapping" the globals in a struct and using macros to
access fields (posibly with mutex/semaphores) was a good idea - but
after you last statement, I am not sure if you mean it is a 'hack' -
since you have been down this route already, your views are of interest
to me ...
Anything to do with the "posibly [sic] with mutex/semaphores"
part belongs in comp.programming.threads, not here, since as I
mentioned before the C language's limited notions of simultaneity
don't extend to multi-threaded or even multi-process programs.

Now, about macros of the kind I illustrated. The first bit
of hackishness is the introduction of an "assumed" identifier,
the `gp->' part. If the function argument isn't named literally
`gp' the thing blows up. A person reading the code will see an
argument named `gp' but will not see it used anywhere in the
body of the function, and may get confused by the apparently
unused argument. A person studying the code in a debugger will
be unable to examine or alter what seems on its face to be a
perfectly normal `global2' variable. And so on. You may save
some lexical work up front, but with those savings you buy a
nagging irritation that keeps on paying its unwelcome dividends.

A coding style guide developed by a bunch of pretty highly-
respected C folks puts it this way: "Don't change syntax via
macro substitution. It makes the program unintelligible to all
but the perpetrator." You, it seems, are even now struggling
with the decisions made by the long-ago authors of your "legacy
code," and probably complaining or at least muttering about the
lack of clarity resulting from some of those decisions. Have
mercy on the person who in turn will follow you through this
code, and seek not to be on the receiving end of his mutterings
and complaints. Remember: Source code is read by compilers but
also by programmers, and the latter are the more important
audience.

--
Er*********@sun.com
Jun 27 '08 #8
rio
What is "Reentrant code"?
A function that use all its data in the stack i.e. automatic
variables and arguments, is reentrant?
A function that use all its data that came from a reentrant malloc
function is reentrant?

Thank you

Jun 27 '08 #9
In article <48***********************@reader4.news.tin.it>, rio <a@b.cwrote:
>What is "Reentrant code"?
A function that use all its data in the stack i.e. automatic
variables and arguments, is reentrant?
A function that use all its data that came from a reentrant malloc
function is reentrant?
Suppose a routine is executing, and -somehow- during the middle
of execution, the execution gets suspended and -somehow- the
same routine gets called *while the original is suspended*.
Then if the routine works properly in the new call and exits
clealy, and upon being resumed, the suspended version continues
to work properly, then the routine was re-entrant.

In the above discussions, I omitted the mechanisms by which
the execution of a routine might get suspended and yet a new instance
of it be started. In pure standard C, the only way it can happen is
if the routine is called (directly or indirectly) as a signal handler
as a result of a system-generated signal or as a result of a raise()
call. In C that is not quite as pristine, there may be system
mechanisms (such as threads) that allow such things to happen.
--
"This quitting thing, it's a hard habit to break once you start."
-- Walter Matthau
Jun 27 '08 #10
On Jun 17, 9:49*am, rober...@ibd.nrc-cnrc.gc.ca (Walter Roberson)
wrote:
In the above discussions, I omitted the mechanisms by which
the execution of a routine might get suspended and yet a new instance
of it be started. In pure standard C, the only way it can happen is
if the routine is called (directly or indirectly) as a signal handler
as a result of a system-generated signal or as a result of a raise()
call.
You forgot recursion.
Jun 27 '08 #11
In article <57**********************************@k30g2000hse. googlegroups.com>,
Kaz Kylheku <kk******@gmail.comwrote:
>On Jun 17, 9:49=A0am, rober...@ibd.nrc-cnrc.gc.ca (Walter Roberson)
wrote:
>In the above discussions, I omitted the mechanisms by which
the execution of a routine might get suspended and yet a new instance
of it be started. In pure standard C, the only way it can happen is
if the routine is called (directly or indirectly) as a signal handler
as a result of a system-generated signal or as a result of a raise()
call.
>You forgot recursion.
I was thinking about recursion, but I got up hours too early today and
somehow it dropped out of my thought processes when I was composing that.

Recursion is relevant to the topic of re-entrancy; I think I was
focussed a little too much on the "suspension" in terms of interruption,
especially as in the other cases (signals, threads) the routine is left
in an indeterminate state, whereas in recursion there is always a
sequence point before the call so the routine's state will be more
stable.
--
"The slogans of an inadequate criticism peddle ideas to fashion"
-- Walter Benjamin
Jun 27 '08 #12
On Jun 17, 12:02 pm, Keith Thompson <ks...@mib.orgwrote:
vipps...@gmail.com writes:

[...]Re-entrancy doesn't have to do anything with threads or thread safety,
but indeed ISO C doesn't mention thing such as 'reentrancy', 'threads'
etc.

[...]

It does, but only briefly.

C99 7.1.4p4:

The functions in the standard library are not guaranteed to be
reentrant and may modify objects with static storage duration.

footnote:

Thus, a signal handler cannot, in general, call standard library
functions.

There's also an index entry for "reentrancy", pointing to 5.1.2.3
(Program execution) and 5.2.3 (Signals an interrupts) as well as
7.1.4.
Thanks Keith, it's just one of those things I /think/ I know about C.
I'm glad people here spot them. I'll remember it from now on.
Jun 27 '08 #13

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

2 posts views Thread by Adrian Parker | last post: by
6 posts views Thread by junky_fellow | last post: by
2 posts views Thread by TheOne | last post: by
9 posts views Thread by TheOne | last post: by
2 posts views Thread by a | last post: by
1 post views Thread by jj_online | last post: by
17 posts views Thread by fmassei | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.