Re: A question of programming technique in C

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Paul Hsieh

    Re: A question of programming technique in C

    On Nov 4, 12:24 pm, c...@tiac.net (Richard Harter) wrote:
    Suppose we are writing a program in C and that we have a
    particular kind of object (in the sense of OO programming) that I
    will call bobbles.  Being well disciplined programmers (Ha!) we
    create a module to package our bobble related functions together.
    >
    If we were using an OO based language there would be machinery in
    place, constructors, destructors, and such like, to handle the
    details of our module, but in C we have to roll our own.
    >
    One of the things we need to do is to create functions that will
    serve as constructors and destructors.  There are two things that
    we have to take care of, specifying the interface, and writing
    the function internals.  My concern is with specifying the
    interface.
    >
    I am going to throw in one curve ball.  Our program will contain
    references to created bobbles; we won't know where these are so
    they can go stale when we delete bobbles.  The code will need to
    be able to detect that a reference has gone stale and deal with
    it appropriately.
    This is the whole idea behind "opaque handles". The problem is that
    you need to allow for the possibility of live handles and obsolete
    handles having a mixed existence during the lifetime of your program.

    That being said, there is a typical "good enough for the real world"
    solution that you can implement in pure C. You need a central "bobble
    object management interface". The idea is that you maintain a single
    instance shared hash table which maps from integers to bobble
    pointers. So you would hash:

    struct bobbleMap {
    int64_t handle; /* INDEX */
    struct bobble * entry; /* pointer to the real Bobble */
    };

    indexed by the value of handle (so the hash function can be trivial if
    you want it to be). You would, as you suggest, issue new handles from
    some incrementing counter. The point being that you have to hope that
    the lifetime of your program is definitively shorter than the time it
    takes for a 64-bit counter to wrap around (otherwise, this solution is
    unsound).

    In your main program you would just hang onto the 64 bit handle, and
    pass it alone as the parameter to any operation you wanted to do on
    the bobble. So each operation would first look up the bobble by
    handle in the central hash table, and return an error code if the
    entry was NULL, otherwise proceed with that operation on the actual
    entry and return with whatever error code that operation issues
    (hopefully success).

    So you can implement a "closeHandl e()" (which would modify the hash
    table entry from a given handle to point to NULL after destroying the
    bobble it was pointing to if any) function instead of a
    destroyBobble() which would not exist as a public function even though
    you would still create some sort of int64_t createBobble (...)
    function. But to make sure that your system was sound you could still
    implement a destroyAllBobbl es (...) which would wipe everything out
    (in the central hash table) at the "end of your program".

    The problem is that this can be slow because you need to do a hash
    look up every time you do something with your bobble.

    Another solution is to literally track the references to each bobble
    as you get references to them. So:

    struct bobbleDescripto r {
    struct bobbleDescripto r * prev; /* LINKED LIST OF DESCRIPTORS
    */
    struct bobbleDescripto r * next; /* LINKED LIST OF DESCRIPTORS
    */
    struct bobble * entry; /* pointer to the real bobble
    */
    };

    And you would gain access to each now descriptor from a previous
    descriptor:

    int openDescriptor (struct bobbleDescripto r * outBp,
    struct bobbleDescripto r * inBp);

    which you would use via:

    struct bobbleDescripto r bd;

    if (0 <= openDescriptor (&bd, oldDescriptor)) {
    /* Success; I now use &bd as my handle to operations
    modifying the bobble it references. */
    }

    This *inserts* the bd into the linked list described by the
    oldDescriptor and points directly to the bobble (so its faster than a
    hash table look up, for example.) You would "self remove" the
    descriptor via:

    int closeDescriptor (struct bobbleDescripto r * bp);

    which would remove itself from the linked list and NULL out its own
    references for safety reasons. Then finally you would create a
    destructor:

    int destroyBobble (struct bobbleDescripto r * bp);

    which would walk the linked list and overwrite their bobble entries to
    NULL, then free the bobble. So after calling this all the descriptors
    for that bobble would have pointers to NULL instead of the bobble that
    they used to be pointing to. In this way we can see how the lifetime
    of a reference to a bobble can exceed that of the bobble itself.

    It would almost be like how file handles are used in C today, if you
    could imagine opening up a file multiple times and having the
    underlying OS delete the file out from underneath you while all those
    file handles were still open. Different OSes have different behaviors
    if you do that, but ignore than and concentrate on the API and how it
    would have to be implemented if it allowed for multiple handles being
    opened on the same file and an external actor deleting the file on
    you.

    The final alternative which I am surprised Jacob Navia isn't harping
    on here, obviously, is to use garbage collection and not free the
    bobble at all (just put a flag in it that indicates that further use
    of it is invalid). This is obviously not portable or implementable
    with standard C, but you can see how this isn't even a serious
    question in a GC language.

    --
    Paul Hsieh
    Pobox has been discontinued as a separate service, and all existing customers moved to the Fastmail platform.


  • Bartc

    #2
    Re: A question of programming technique in C

    Paul Hsieh wrote:
    On Nov 4, 12:24 pm, c...@tiac.net (Richard Harter) wrote:
    >I am going to throw in one curve ball. Our program will contain
    >references to created bobbles; we won't know where these are so
    >they can go stale when we delete bobbles. The code will need to
    >be able to detect that a reference has gone stale and deal with
    >it appropriately.
    The final alternative which I am surprised Jacob Navia isn't harping
    on here, obviously, is to use garbage collection and not free the
    bobble at all (just put a flag in it that indicates that further use
    of it is invalid). This is obviously not portable or implementable
    with standard C, but you can see how this isn't even a serious
    question in a GC language.
    I thought the main point was the possibility of references to deleted
    objects still existing, and being able to trap attempts to use those.

    With GC surely the objects would never be deleted in that case, and would
    continue to occupy memory.

    --
    Bartc


    Comment

    • Paul Hsieh

      #3
      Re: A question of programming technique in C

      On Nov 5, 4:05 pm, "Bartc" <b...@freeuk.co mwrote:
      Paul Hsieh wrote:
      On Nov 4, 12:24 pm, c...@tiac.net (Richard Harter) wrote:
      I am going to throw in one curve ball. Our program will contain
      references to created bobbles; we won't know where these are so
      they can go stale when we delete bobbles. The code will need to
      be able to detect that a reference has gone stale and deal with
      it appropriately.
      The final alternative which I am surprised Jacob Navia isn't harping
      on here, obviously, is to use garbage collection and not free the
      bobble at all (just put a flag in it that indicates that further use
      of it is invalid).  This is obviously not portable or implementable
      with standard C, but you can see how this isn't even a serious
      question in a GC language.
      >
      I thought the main point was the possibility of references to deleted
      objects still existing, and being able to trap attempts to use those.
      >
      With GC surely the objects would never be deleted in that case, and would
      continue to occupy memory.
      Well, not necessarily. If it were implemented as:

      typedef struct tagBobbleHolder {
      struct bobble * entry;
      } * bobbleHandle_t;

      And you would create a struct tagBobbleHolder for each bobble, and use
      a pointer to it (i.e., bobbleHandle_t) as your handle with the
      solutions I outline above. Then we would be set. So:

      int action (bobbleHandle_t bp) {
      if (!bp || !*bp) return ERR_BOBBLE_NOT_ ALIVE;
      return realAction (bp->entry);
      }

      int closeBobble (bobbleHandle_t bp) {
      if (!bp || !*bp) return ERR_BOBBLE_NOT_ ALIVE;
      bp->entry = NULL; /* GC can clean bobble immediately */
      bp = NULL; /* GC will get around to cleaning the refs */
      return ERR_BOBBLE_OK; /* == 0 */
      }

      void server (bobbleHandle_t bp) {
      bobbleHandle_t lbp = bp;
      someFunctionWhi chDoesWhoKnowsW hat (lbp);
      action (lbp);
      lpb = NULL;
      }

      So we can see that no matter what someFunctionWhi chDoesWhoKnowsW hat ()
      does, including closing *lpb the GC will tear things down exactly as
      they should with the only "wasted memory" the extra pointers that
      these guys are all passing around. But that becomes proportional to
      the number of containers, or threads or whatever which clearly are
      already wasting more than that amount of memory by their very
      existence.

      --
      Paul Hsieh
      Pobox has been discontinued as a separate service, and all existing customers moved to the Fastmail platform.


      Comment

      Working...