Ah the lovely amount of objects
Posted: Sun May 14, 2006 4:17 pm
So here we go, I'm settle on the following for ipc handle management for now:
There are objects, that can be sent message, which are really sent to thread's messagequeue, and object pointer becomes part of the message, allowing dispatch. You are supposed to have an object like this:
If you don't use DispatchMessage() which is part of the messaging API, then it's just a userdata pointer for you.
Since sending a message involves an object, and a thread, the latter of which might die (taking the former with it, ofcourse), there are proxies, that are used to resolve the object and thread, and fail if the thread has died.
I don't want to keep dead thread in memory though, neither I want to keep a list of proxies pointing a particular thread. So there's a "lifestone" for each thread. This is a small object, with the sole purpose of being left behind after thread death, until the last proxy pointing to that thread is closed.
Finally, to find a proxy, userspace thread sends an handleID, which is simply an index into a per-process handle-table, which contains pointers to proxies. Since there can be several handles to single object, proxy is necessary to allow notifying the owning thread when the last handle is destroyed (allowing it to manage it's own resources like memory).
Ignoring the actual objects (which are not really involved in low-level messaging) I have:
- lifestones (one created per thread)
- threads (which have message queues)
- proxies (which actually represent certain objects)
- handles (which are used to check access)
This setup allows me to:
- Tell an object's owner when last handle is destroyed.
- Free the (rather large) thread structure when thread dies.
- Figure out if some thread has died.
- Not keep any reverse lookup tables, lists, or anything.
- Avoid security hazards from reusing thread numbers or such.
Limitations:
- If for some reason "object free" message can't be sent (say, full queue) then the object owner might accumulate garbage. Might try solving this another day.
- A small lifestone object stays in memory, until last handle is freed. Since at any given time there are finite number of threads (memory based, calculated limit), each process has at least one thread, and each process has finite number of handles (by design), there will only be finite number of lifestones in memory, but that limit can be quite large.
So to send a message, the system:
1) checks process table if given handleID is valid (fail if not)
2) follows the handle to find a proxy
3) adds the "object" from proxy to message
4) follows a pointer from proxy to a lifestone
5) checks if the stone's thread is alive (fail if not)
6) follows a pointer from lifestone to thread
7) put the message in thread's message queue
Simple, isn't it.
PS. Above ignores any handle/memory copies possibly made.
I'm not surprised at all that most systems try to get away with a security model more coarse-grained than a full object-capability system.
There are objects, that can be sent message, which are really sent to thread's messagequeue, and object pointer becomes part of the message, allowing dispatch. You are supposed to have an object like this:
Code: Select all
typedef struct myObj_S myObj;
struct myObj_S {
void dispatch(myObj*, MWORD op, MWORD w1, MWORD w2);
/* add below whatever other data you need */
};
Since sending a message involves an object, and a thread, the latter of which might die (taking the former with it, ofcourse), there are proxies, that are used to resolve the object and thread, and fail if the thread has died.
I don't want to keep dead thread in memory though, neither I want to keep a list of proxies pointing a particular thread. So there's a "lifestone" for each thread. This is a small object, with the sole purpose of being left behind after thread death, until the last proxy pointing to that thread is closed.
Finally, to find a proxy, userspace thread sends an handleID, which is simply an index into a per-process handle-table, which contains pointers to proxies. Since there can be several handles to single object, proxy is necessary to allow notifying the owning thread when the last handle is destroyed (allowing it to manage it's own resources like memory).
Ignoring the actual objects (which are not really involved in low-level messaging) I have:
- lifestones (one created per thread)
- threads (which have message queues)
- proxies (which actually represent certain objects)
- handles (which are used to check access)
This setup allows me to:
- Tell an object's owner when last handle is destroyed.
- Free the (rather large) thread structure when thread dies.
- Figure out if some thread has died.
- Not keep any reverse lookup tables, lists, or anything.
- Avoid security hazards from reusing thread numbers or such.
Limitations:
- If for some reason "object free" message can't be sent (say, full queue) then the object owner might accumulate garbage. Might try solving this another day.
- A small lifestone object stays in memory, until last handle is freed. Since at any given time there are finite number of threads (memory based, calculated limit), each process has at least one thread, and each process has finite number of handles (by design), there will only be finite number of lifestones in memory, but that limit can be quite large.
So to send a message, the system:
1) checks process table if given handleID is valid (fail if not)
2) follows the handle to find a proxy
3) adds the "object" from proxy to message
4) follows a pointer from proxy to a lifestone
5) checks if the stone's thread is alive (fail if not)
6) follows a pointer from lifestone to thread
7) put the message in thread's message queue
Simple, isn't it.
PS. Above ignores any handle/memory copies possibly made.
I'm not surprised at all that most systems try to get away with a security model more coarse-grained than a full object-capability system.