Reentrant code

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Bit Byte

    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.
  • rahul

    #2
    Re: Reentrant code

    On Jun 17, 12:10 pm, Bit Byte <r...@yourbox.c omwrote:
    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>

    Comment

    • vippstar@gmail.com

      #3
      Re: Reentrant code

      On Jun 17, 11:33 am, rahul <rahulsin...@gm ail.comwrote:
      On Jun 17, 12:10 pm, Bit Byte <r...@yourbox.c omwrote:
      >
      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.progr ammer.
      <snip rahul's off-topic reply>

      Comment

      • Keith Thompson

        #4
        Re: Reentrant code

        vippstar@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_Keit h) kst-u@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"

        Comment

        • jacob navia

          #5
          Re: Reentrant code

          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

          Comment

          • Eric Sosman

            #6
            Re: Reentrant code

            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.programmin g.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(stru ct 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
            esosman@ieee-dot-org.invalid

            Comment

            • Bit Byte

              #7
              Re: Reentrant code

              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.programmin g.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(stru ct 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 ...

              Comment

              • Eric Sosman

                #8
                Re: Reentrant code

                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.programmin g.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.

                --
                Eric.Sosman@sun .com

                Comment

                • rio

                  #9
                  Re: Reentrant code

                  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



                  Comment

                  • Walter Roberson

                    #10
                    Re: Reentrant code

                    In article <4857e613$0$416 56$4fafbaef@rea der4.news.tin.i t>, 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

                    Comment

                    • Kaz Kylheku

                      #11
                      Re: Reentrant code

                      On Jun 17, 9:49 am, rober...@ibd.nr c-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.

                      Comment

                      • Walter Roberson

                        #12
                        Re: Reentrant code

                        In article <576bc88e-80d6-43cc-8e7d-0048a41da912@k3 0g2000hse.googl egroups.com>,
                        Kaz Kylheku <kkylheku@gmail .comwrote:
                        >On Jun 17, 9:49=A0am, rober...@ibd.nr c-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

                        Comment

                        • vippstar@gmail.com

                          #13
                          Re: Reentrant code

                          On Jun 17, 12:02 pm, Keith Thompson <ks...@mib.orgw rote:
                          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.

                          Comment

                          Working...