On Nov 4, 12:24 pm, c...@tiac.net (Richard Harter) wrote:
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
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.
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.
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
Comment