[Oberon] Oberon for a C++ user.

Andreas Pirklbauer andreas_pirklbauer at yahoo.com
Mon Oct 3 10:01:10 CEST 2016


Overloading is not the problem here. It would be straightforward to modify the code generator for the assignment operator := to care of reference counting when it is applied to a procedure variable, i.e. implement what you call ASSIGN and RETRACT. In fact, I have implemented just that on Ceres a long time ago as part of (the original version of) Experimental Oberon. But I do not recommend to use reference counting in  general. Reason: it introduces a hidden mechanism (not desired) and a non-negligible overhead in assignments (ok, one may argue that this is “only" done for procedure variables which are typically assigned only once, but still - it is in the spirit of Oberon to avoid hidden mechanisms).


There is an alternative solution without reference counting: When unloading a module M using Modules.Free(M), one *could* traverse the heap object for object - just like the garbage collector does - and check whether the encountered objects contain procedure variables, and if so see whether the (addresses of) such procedure variables belong to M's code section (i.e. whether M.code <= address < M.imports). If there is at least one such procedure variable, simply don't unload M. It's practically the same code as the garbage collector (so it could in fact be be combined with it), except that is it merely used to *check* whether the addresses of procedure variables in heap records belong to (the code section of) module M. That too I have implemented on Ceres (in NS32032 assembly code) a long time ago, and now that the garbage collector is itself implemented in Oberon (see procedures Kernel.Mark and Kernel.Scan), it would be straightforward to bring the code forward to Oberon 2013 - one really just needs to create a modified version of Kernel.Mark and call it from within procedure Modules.Free. But again, this would create a non-negligible overhead during unloading of a module - every time a module M needs to be unloaded, the *entire* heap would need to be traversed, and this just to find out whether one or more of its elements contain a procedure variable whose memory addresses belong to module M's code (this inefficiency can be remedied though in various ways, e.g. by setting a flag in module M when an assignment of M.proc to a procedure variable is made, but still, this would make both assignments and unloading rather cumbersome operations). But it is definitely doable. Note that performance is not really an issue, because unloading of modules should be rare - and could be preceded by a garbage collection run as a matter of course (in which case one just needs to slightly adapt *its* code).


Thus, it is doable and has in fact been implemented. But I have yet to see a truly satisfactory and elegant solution to this problem - and this is why I haven’t brought the code forward to Oberon 2013 (and Experimental Oberon 2013).


Andreas

From: "Skulski, Wojciech" <skulski at pas.rochester.edu>
To: Andreas Pirklbauer <andreas_pirklbauer at yahoo.com>; ETH Oberon and related systems <oberon at lists.inf.ethz.ch> 
Sent: Monday, October 3, 2016 9:07 AM
Subject: RE: [Oberon] Oberon for a C++ user.

Andreas:

the problem IMHO is in the operator :=, which is overloaded. The procedure variables are not a variables in the usual sense. The operator := should not apply to them.

A regular variable has a value. The value is passive. It gets acted upon by a client. The variable is not doing anything on its own. Somebody is doing something with the variable (somebody is using its value for some goal).

IF var1 = var2 THEN... (*passive role of the variables*)

The procedure variable has a value (the address to jump to), and also a behavior. The value is passive, but the behavior is active. The value is retained after unloading the implementation, but the behavior is damaged. This is the problem. 

The value (the address) is not that important. Yes, you can use it in comparisons:

IF proc1 = proc2 THEN... (*passive role of the procedure variables*)

Note that after unloading the implementations of the proc1 and proc2, you can still make the comparison. Both proc1 and proc2 are perfectly valid in the passive sense. Their code is gone, but their passive values (addresses) are perfectly OK.

But this is not why procedure variables are important. We want their behaviors. And this gets damaged upon unloading.

In order to cure the problem, the operator :=should not be permitted with procedure variables. There needs to be another, separate operator which will assign procedure variables and also take care of the reference counting.

ASSIGN (procvar, Mod.Proc)

I think that separating both operators := and ASSIGN will make sense, because regular variables are not posing the problem which procedure variables are posing. 

The counterpart of ASSIGN would be RETRACT (or some better name). Since you re not allowed to use := with procedure variables, you could not write 

procvar := NIL; (*not allowed*)

You would have to say 

RETRACT (procvar); (*takes care of the references*)

I hope this proposition makes sense.

W.

----------------------------

Chris, Joerg:

that is exactly the problem: assigning a procedure of a *client* module to a procedure variable of an *imported* module, and then unloading the client module - leaving the procedure variable in the imported module dangling.

Assigning a procedure of a client module to a variable of an imported "base" module is in fact the standard way the Oberon system itself employs procedure variables to implement the viewer system in an object-oriented style.

For example, module TextFrames (the client) installs a handler (procedure TextFrames.Handle) in the field F.handle of text frame F (see TextFrames.NewText and TextFrames.Open for the details), where the record field F.handle is declared as a procedure variable in the "base" module Viewers (see Viewers.ViewerDesc), which also manages the viewer data structure (see procedure Viewers.Open).

Except that of course in the case of the Oberon system, the "client" module TextFrames *never* gets unloaded, so this is not a problem there. Thus, the burden is on the user to make sure that such modules never get unloaded.

It is of course possible to implement a "procedure variable" counter - in analogy to the "module counter" employed in Oberon: each time an assignment to a procedure variable is made, the counter gets increased. But that would require changing the code generator compiler for assignments, and the module loader (Modules.Load) and unloader (Modules.Free). But I would refrain from introducing such complexity to the compiler and runtime system.

Andreas

Chris Burrows chris at cfbsoftware.com <mailto:oberon%40lists.inf.ethz.ch?Subject=Re:%20Re%3A%20%5BOberon%5D%20Oberon%20for%20a%20C%2B%2B%20user.&In-Reply-To=%3C000a01d21ba8%24b97dc9d0%242c795d70%24%40cfbsoftware.com%3E>

________________________________

> Joerg,
>
> OK - you may well be onto something here but it is incomplete as it is. Something has to

> import Square so that the module body is executed, or you need an exported procedure in

> Square that is called from the OS.

>
> Note: there are a couple of semicolons missing and Init must be exported.

>
> Chris.

> -----Original Message-----
> From: Jörg Straube [mailto:joerg.straube at iaeth.ch<https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.inf.ethz.ch_mailman_listinfo_oberon&d=CwMFaQ&c=kbmfwr1Yojg42sGEpaQh5ofMHBeTl9EI2eaqQZhHbOU&r=uUiA_zLpwaGJIlq-_BM9w1wVOuyqPwHi3XzJRa-ybV0&m=UHr5AP3cXC2ZAXacD2dYANUhpxDnR6gWosSpUd2oaRw&s=TWgxIZxS5z7mpZJDUj92ydjRoic3rLYdp0iN4Zb0ngQ&e= <https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.inf.ethz.ch_mailman_listinfo_oberon&d=CwMFaQ&c=kbmfwr1Yojg42sGEpaQh5ofMHBeTl9EI2eaqQZhHbOU&r=uUiA_zLpwaGJIlq-_BM9w1wVOuyqPwHi3XzJRa-ybV0&m=UHr5AP3cXC2ZAXacD2dYANUhpxDnR6gWosSpUd2oaRw&s=TWgxIZxS5z7mpZJDUj92ydjRoic3rLYdp0iN4Zb0ngQ&e=>>]
> Sent: Saturday, 1 October 2016 2:22 PM
> To: chris at cfbsoftware.com<https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.inf.ethz.ch_mailman_listinfo_oberon&d=CwMFaQ&c=kbmfwr1Yojg42sGEpaQh5ofMHBeTl9EI2eaqQZhHbOU&r=uUiA_zLpwaGJIlq-_BM9w1wVOuyqPwHi3XzJRa-ybV0&m=UHr5AP3cXC2ZAXacD2dYANUhpxDnR6gWosSpUd2oaRw&s=TWgxIZxS5z7mpZJDUj92ydjRoic3rLYdp0iN4Zb0ngQ&e= <https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.inf.ethz.ch_mailman_listinfo_oberon&d=CwMFaQ&c=kbmfwr1Yojg42sGEpaQh5ofMHBeTl9EI2eaqQZhHbOU&r=uUiA_zLpwaGJIlq-_BM9w1wVOuyqPwHi3XzJRa-ybV0&m=UHr5AP3cXC2ZAXacD2dYANUhpxDnR6gWosSpUd2oaRw&s=TWgxIZxS5z7mpZJDUj92ydjRoic3rLYdp0iN4Zb0ngQ&e=>>; ETH Oberon and related systems

> Subject: Re: [Oberon] Oberon for a C++ user.
>
> Chris
>
> Here a possble example that might cause issues:
>
> MODULE Figure;
> TYPE
>  DrawProc = PROCEDURE;
>  Figure = POINTER TO FigureDesc;
>  FigureDesc = RECORD
>    next: Figure;
>    draw: DrawProc
>  END;
> VAR list: Figure;
> PROCEDURE Init(d: DrawProc)
>  VAR f: Figure;
>  BEGIN
>    NEW(f); f.next := list; list := f;
>    f.draw := d
>  END Init;
> BEGIN list:= NIL END Figure.
>
> MODULE Square;
> IMPORT Figure;
> PROCEDURE DrawSquare;
>  BEGIN (* do what ever you
>    need to do to draw a square *)
>  END DrawSquare
> BEGIN
>  Figure.Init(DrawSquare)
> END Square.
>
> Now you could run Square and unload Square. The list in Figure has a
> reference to unloaded code.
>
> J rg
>


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.inf.ethz.ch/pipermail/oberon/attachments/20161003/f4951b22/attachment-0001.html>


More information about the Oberon mailing list