start service tasks separately in-case platforms need to perform additional set-up...
[AROS.git] / rom / oop / oop.doc
blob997c1e1cbd9cbde3d84f434994fdd7d850e86af6
1 Overview:
2 This document tries to explain the choices that
3 are made in deciding the design of the new OOP
4 system, which will be used fo (among other things)
5 the HIDDs.
7 General design goals:
9 The system should..
11 - have fast method invocation. Especially when calling the same method
12 on the same objetc many times. (Eg. GetPixel()/SetPixel())
15 - allow non-centralized allocation of IDs. This means that there is no
16 agency somewhere in the world where you have to allocate an ID. IDs
17 are allocated at runtime.
20 - be location transparent. Ie. you invoke a
21 method the same way as for a local objet, even if
22 the object is owned by another task, or is
23 located on a different machine.
26 - support transparent object migration. This means that the object, the
27 system or the user can decide that the object should be moved or copied
28 to another host. Uses: If an object is accessed more often from a remote
29 host than locally, it can create a copy of itself. If the object is deleted
30 locally, it can move itself to the remote host.
33 - Exceptions.
36 - Thread safe method invocation.
39 Definitions:
41 Class - A collection of Interfaces and a description of the Objects
42 of this Class.
44 Object - Instance of a class. If you think of a Class as a blueprint of
45 something, then an Object is what you get when you build the something
46 after the instructuctions of the blueprint.
48 Method - A method is a function which is called with a class, an object
49 and a message as arguments.
51 Interface - An interface is a view of an object. Interfaces are
52 defined globally. A class can implement any number of Interfaces.
53 An Interface contains a table of Methods.
55 Method Stub - This is a normal C function which is called with an
56 object and a message. It is used to simplify the call of a method.
57 The Method Stub extracts the class from the Object and the Interface
58 from the Full Method ID and invokes the Method.
60 Method Base - The lowest ID of all methods on an Interface. Along with the
61 Method Count, this is used to determine if a method if supported by an
62 Interface. This is a constant during the lifetime of the system.
64 Method Count - The number of methods of an Interface. This value
65 can change (it is possible to add methods to an interface during
66 runtime).
68 Interface ID - The ID of an Interface. It is used to look up an
69 Interface when a Method should be invoked.
70 This is a constant during the lifetime of the system.
72 Method Offset - The offset to the Method Base for a Method. Always
73 positive. This is a constant during the lifetime of the system.
75 Method ID - The addition of Method Base and Method Offset for a specific
76 Method. This is a constant during the lifetime of the system.
77 If you have an Interface Object, you can call the method.
79 Full Method ID - The addition of Method ID and the Interface ID. The
80 Interface ID is used to look up the Interface and then the Method ID is
81 used to call the method in the Interface. This is a constant during the
82 lifetime of the system.
84 Method Name - A string with the name of a method.
86 Method Object - An Object used to store a Method to allow to invoke
87 the method fast.
89 Interface Object - An object used to store an Interface
90 which allows you to invoke all methods of that interface fast.
93 Possible solutions to design goals w/pros & cons
95 1) Invocation speed.
96 -------------------
97 For having ultimately fast method invocation we supply method objects.
99 Example of usage of method objects:
101 -------------
103 struct HIDDP_Gfx_SetPixel sp_msg;
105 struct TagItem mobj_tags[] =
107     {A_Method_TargetObject, (IPTR)gfxhidd },
108     {A_Method_Message,      (IPTR)&sp_msg},
109     {A_Method_MethodID,     HIDDM_Gfx_SetPixel},
110     {TAG_DONE, }
114 /* If you don't pass a message, then memory for one could be allocated,
115 and disposed in DisposeObject(spmethod).
116 The msg's methodid will be initialized by NewObject.
117 If you want to use other msg mem, then you
118 can use the Set method on the method object.
121 spmethod = NewObject(NULL, METHODCLASS, mobj_tags);
125 sp_msg.Pen = 0;
126 for (x = 0; x < 100; x ++)
128     for (y = 0; y < 100; y ++)
129     {
130           sp_msg.X = x;
131           sp_msg.Y = y;
133           /* CallMethod() might be a macro */
134           CallMethod(spmethod);
136     }
139 -------------
141 For normal method invocation there are generally two approaches:
143 a) Hashing single methods.
144 b) Hashing interfaces, where an interface
145 is an array of methods (kinda like a AmigaOS library
146 jump table.)
148 a) is a bit faster than b)
150 In order to avoid having to hash the methods, all methods of a class
151 are in a single table. This includes the methods of all parent classes
152 so these can also be called directly (no need for DoSuperMethod()
153 in the dispatcher).
155 For this to work, we need:
157 - A method string -> ID to convert the method name to the method ID
159 - When you allocate a new class, you must allocate a table (size =
160 parentClass->numMethods + self->numMethods), copy the parent method
161 table and overwrite/add the own methods to the table
163 - The user uses a global variable in the classbase as the basic offset
164 to call methods of this class. The header file defines the offsets.
166 Interfaces work as usual but they store maybe only the real methodID
167 (one lookup more and one method object less).
169 Caveats:
171 - When a parent class changes its method table, then the child classes
172 won't notice. This could be avoided it we copy method objects.
174 - the DoMethod() stub must do an add for each method invokation but
175 no hashing.
177 Interfaces can be implemented like this:
179 - One hash table which contains tables of methods/method IDs and the
180 key is the interface ID.
182 - One hash table which contains method objects and the key is the
183 computed interface+method ID.
185 2) Noncentralized ID allocation.
186 -------------------------------
187 To allow for this, we avoid fixed integer
188 IDs (like in BOOPSI).
189 Instead we use global variables for
190 IDs.
192 Two ways to allocate IDs:
195 Store interface IDs in global vars, and generate method IDs on the fly as
196 sum of interface ID and a offset ID for the method.
198 Example:
200 extern ULONG __OOPI_Root;
202 #define I_Root  __OOPI_Root
203 #define MIDX_Root_New           0
204 #define MIDX_Root_Dispose       1
206 #define M_Root_New      (I_Root + MIDX_Root_New)
207 #define M_Root_Dispose  (I_Root + MIDX_Root_Dispose)
209 Pro:
210 - Uses little mem.
211 - Relatively little CPU time used for initialization.
213 Con:
214 - uses more CPU time for initialization.
217 Use one global var for each methodID.
219 Pro:
220 - Faster pr. call.
221 - Leaves more flexibility for future changes.
222 - Allows to specify each method as a string
223 in MakeClass().
225 Con:
226 - Uses more mem.
227 - Very hard to initialize
230 As for AttrIDs, there is a problem with using II), because then one can't
231 use switch() to parse AttrIDs.
233 Solution: Use methods for set & get of each attribute. which will use some
234 mem. Con: Clumsy.
236 To avoid the problems with init, we can use a global ClassBase (like a
237 library base) and use a public field in there to store the offsets. As for
238 attributes, a class should allocate a fixed number of IDs for its
239 attributes and store the base in the ClassBase. The header file then
240 contains offsets for each Attribute. In the class, you can very quickly
241 check if an attribute is part of the class:
243     ULONG id = tag->ti_Tag - class->cl_attrBase;
245     if (id < class->cl_numAttrs)
246         ... handle own attributes
247     else
248         ... handle attributes of other classes
250 Location Transparent and Object Migration
251 -----------------------------------------
253 To allow these two, we use method objects. Method objects
254 contain the following attributes:
256 - A pointer to the method
257 - The method ID
258 - The class
259 - The object
260 - If several objects are created for the same method and the
261 method, for which they were created, changes, then all other
262 method objects change as well.
263 - The Method class contains a DoMethod() which is used to
264 call the method stored in the object.
266 By overloading DoMethod() of the Method class, you can implement
267 thread safe objects, migrate objects to other computers, etc.
270 Exceptions
271 ----------
273 The easy part is to create the Exception class and the objects.
275 Now the hard part:
277 - How do I get the position information (stack frames, function names,
278 local variables (?), line numbers, filename) ?
280 - How do I "throw" an exception ?
282 - How do I catch an exception ?
285 On Unix, I can use GNU C extensions for most things. With a special
286 assembler, it should be possible to create position information and update
287 the call stack.
289 To throw and catch an exception, I can use a global array which contains
290 jmp_bufs and catched exceptions and throw just calls a global function
291 which looks through the array for a matching catch and does a longjmp()
292 to the place. We can use macros to throw and catch exceptions.
295 On Amiga, we might be able to use similar techniques. The globals
296 must be stored in the ETask info.
298 Thread safe method invocation.
299 -----------------------------
301 This can be done through the use of proxy objects
302 and IPC, where the server runs inside one task,
303 an the clients (other tasks) get proxies to the object
304 residing inside the server.
305 The server will the Wait() for incoming
306 methods and execute them on the proxied
307 object. Currently the system is using Exec messages,
308 and since AROS has no MP (yet), I only have to pass a
309 pointer.
312 A problem that has to be addressed here is
313 deadlocks. Say task A has object a, and task B
314 has object b.
316 Now, A calls a method on b, but the method calls another
317 method on a. Since A will be Wait()ing for a response from
318 B, then it can't execute the method, and both
319 tasks will be waiting forever.