An idea about better finalization support

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

An idea about better finalization support

Igor Stasenko
 
In recent topic on Seaside list, someone has asked, why Seasider's
abandon the idea of using weak references (and use finalization
technique when they die).
The main issue was the slowness of weak finalization process
implemented in Squeak.

The problem:
- sometimes developer needs to get a notification from system when
particular object become garbage, so an additional processing could be
activated, called finalization.

How things currently working:

Squeak provides a default finalization scheme implemented by
WeakRegistry class, where one could register a pair of weak reference
and executor. Its connected with finalization process , which governed
by WeakArray class.

Upon each GC, the finalization process awakes and sending
#finalizeValues to all objects, registered in FinalizationDependents
collection.
When a WeakRegistry receives this message, it iterates over own
WeakIdentityKeyDictionary instance to find a pairs, for which key
(weak ref) became nil, and sends #finalize to the value (executor).

The problem with such approach, is a linear time consumption spent for
scanning all registered key>value pairs.
If you have thousands of objects registered in that way, while only
few of them could become garbage, the finalization process still will
spend a fair amount of time, trying to determine which ones became
garbage.

To eliminate this waste, we need to implement a scheme in VM, which
can help us to reach only those objects which is died during current
GC cycle, leaving rest untouched.

Here is what i'd like to propose:

- add a special class

Object weakSubclass: #WeakReferenceWithNotification
        instanceVariableNames: 'list next'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Collections-Weak'

so, that VM can check this special kind of weak reference in
ObjectMemory>>finalizeReference: method.

A 'list' ivar can point to any object, we only need to be sure it
having at least one slot to hold a list head:

Object subclass: #FinalizationList
        instanceVariableNames: 'head'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Collections-Weak'

now, all we need is to modify #finalizeReference: method, to do
following at the moment of storing nil pointer:

" this is not actually the slang code, just to illustrate what it does "

(oop class isKindOf: WeakReferenceWithNotification) ifTrue: [
  | list |
  list := oop at: ListSlotIndex.
  oop at: NextSlotIndex put: (list at: HeadSlotIndex).
  list at: HeadSlotIndex put: oop.
].


What it does, it simply links given oop to the list.

And as you may suppose, if we have initial state:
- list with head == nil
- multiple instances with WeakReferenceWithNotification , all pointing
to our list, and having a weak refs,

then after GC, if some of the refs become nilled, in a list's head we
will have a list of weak references we need to finalize (or do
anything else we want to).

Then a finalization process in case of WeakRegistry, don't needs to go
through all weak references which are registered in itself, instead it
could simply do:

"suppose we use a subclass "
WeakReferenceWithNotification weakSubclass: #WeakReferenceWithExecutor
        instanceVariableNames: 'executor'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Collections-Weak'
...

ref := list head.
ref notNil whileTrue: [
   ref executor finalize.
   ref := ref next.
].
list head: nil.

P.S. Me hopes ;) , that i successfully shown that the actual changes
to VM is quite simple and minimal. But effects of such change could
have a major impact on a finalization scheme performance.


--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Eliot Miranda-2
 
Igor,

    read up on ephemerons.  They're a fine way of doing instance-based finalization in Smalltalk, designed originally by George Boswith and implemented in Digitalk's Smalltalk.  They're "smart" associations which get queued for finalization when the only references to their key are through ephemerons.  They've been in the VW VM since I implemented them in about 2000, and they're actually being used up in the image now.

On Wed, Apr 22, 2009 at 3:19 PM, Igor Stasenko <[hidden email]> wrote:

In recent topic on Seaside list, someone has asked, why Seasider's
abandon the idea of using weak references (and use finalization
technique when they die).
The main issue was the slowness of weak finalization process
implemented in Squeak.

The problem:
- sometimes developer needs to get a notification from system when
particular object become garbage, so an additional processing could be
activated, called finalization.

How things currently working:

Squeak provides a default finalization scheme implemented by
WeakRegistry class, where one could register a pair of weak reference
and executor. Its connected with finalization process , which governed
by WeakArray class.

Upon each GC, the finalization process awakes and sending
#finalizeValues to all objects, registered in FinalizationDependents
collection.
When a WeakRegistry receives this message, it iterates over own
WeakIdentityKeyDictionary instance to find a pairs, for which key
(weak ref) became nil, and sends #finalize to the value (executor).

The problem with such approach, is a linear time consumption spent for
scanning all registered key>value pairs.
If you have thousands of objects registered in that way, while only
few of them could become garbage, the finalization process still will
spend a fair amount of time, trying to determine which ones became
garbage.

To eliminate this waste, we need to implement a scheme in VM, which
can help us to reach only those objects which is died during current
GC cycle, leaving rest untouched.

Here is what i'd like to propose:

- add a special class

Object weakSubclass: #WeakReferenceWithNotification
       instanceVariableNames: 'list next'
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Collections-Weak'

so, that VM can check this special kind of weak reference in
ObjectMemory>>finalizeReference: method.

A 'list' ivar can point to any object, we only need to be sure it
having at least one slot to hold a list head:

Object subclass: #FinalizationList
       instanceVariableNames: 'head'
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Collections-Weak'

now, all we need is to modify #finalizeReference: method, to do
following at the moment of storing nil pointer:

" this is not actually the slang code, just to illustrate what it does "

(oop class isKindOf: WeakReferenceWithNotification) ifTrue: [
 | list |
 list := oop at: ListSlotIndex.
 oop at: NextSlotIndex put: (list at: HeadSlotIndex).
 list at: HeadSlotIndex put: oop.
].


What it does, it simply links given oop to the list.

And as you may suppose, if we have initial state:
- list with head == nil
- multiple instances with WeakReferenceWithNotification , all pointing
to our list, and having a weak refs,

then after GC, if some of the refs become nilled, in a list's head we
will have a list of weak references we need to finalize (or do
anything else we want to).

Then a finalization process in case of WeakRegistry, don't needs to go
through all weak references which are registered in itself, instead it
could simply do:

"suppose we use a subclass "
WeakReferenceWithNotification weakSubclass: #WeakReferenceWithExecutor
       instanceVariableNames: 'executor'
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Collections-Weak'
...

ref := list head.
ref notNil whileTrue: [
  ref executor finalize.
  ref := ref next.
].
list head: nil.

P.S. Me hopes ;) , that i successfully shown that the actual changes
to VM is quite simple and minimal. But effects of such change could
have a major impact on a finalization scheme performance.


--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

johnmci
In reply to this post by Igor Stasenko
 
Well there was the Ephemerons stuff you can add too

http://map.squeak.org/package/fe2a35f5-3f97-431e-8596-58e810aa3c72

ian had a version he pushed out a couple of years, back which was  
retracted because it
caused VM crashs, but later was proven to be an off by 1 C/slang  
coding error.
But no-one I think had the courage to inflict back onto the community.


Er so how would compatibilty work with this new way given an image  
which may or may not include image side companion code.


--
=
=
=
========================================================================
John M. McIntosh <[hidden email]>
Corporate Smalltalk Consulting Ltd.  http://www.smalltalkconsulting.com
=
=
=
========================================================================



Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Eliot Miranda-2
 


On Wed, Apr 22, 2009 at 3:50 PM, John M McIntosh <[hidden email]> wrote:

Well there was the Ephemerons stuff you can add too

http://map.squeak.org/package/fe2a35f5-3f97-431e-8596-58e810aa3c72

ian had a version he pushed out a couple of years, back which was retracted because it
caused VM crashs, but later was proven to be an off by 1 C/slang coding error.
But no-one I think had the courage to inflict back onto the community.


Er so how would compatibilty work with this new way given an image which may or may not include image side companion code.

We'd still need to support weak arrays.  But Ephemerons allow instance-based finalization, so they're a big step up on the postmortem finalization offered by weak arrays.

The current state of Cog is that with simple code generation and machine-code floating-point primitives the nbody benchmark form the computer language shootout is spending 40% of entire execution time in the incremental GC.  So my next target will be te new two-word object header and a scavenger rather than a deferred code generator.  I'm thinking of looking closely at the V8 GC (Lars Bak's presentation at Microsoft last week) and I could probably add ephemerons to that in a second rev.




--
===========================================================================
John M. McIntosh <[hidden email]>
Corporate Smalltalk Consulting Ltd.  http://www.smalltalkconsulting.com
===========================================================================




Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Igor Stasenko
In reply to this post by Eliot Miranda-2

2009/4/23 Eliot Miranda <[hidden email]>:
>
> Igor,
>     read up on ephemerons.  They're a fine way of doing instance-based finalization in Smalltalk, designed originally by George Boswith and implemented in Digitalk's Smalltalk.  They're "smart" associations which get queued for finalization when the only references to their key are through ephemerons.  They've been in the VW VM since I implemented them in about 2000, and they're actually being used up in the image now.
>

Ephemerons require an additional GC phase, which i think adds much
more complexity to GC comparing to what i propose.
If we care only about instance-based finalization, in its current
state, then i think that what i have propose will do it just well,
except the cases where value refers directly (or indirectly) to key
(so it can't be GCed).

But sure, it would be better to have ephemerons :)

> On Wed, Apr 22, 2009 at 3:19 PM, Igor Stasenko <[hidden email]> wrote:
>>
>> In recent topic on Seaside list, someone has asked, why Seasider's
>> abandon the idea of using weak references (and use finalization
>> technique when they die).
>> The main issue was the slowness of weak finalization process
>> implemented in Squeak.
>>
>> The problem:
>> - sometimes developer needs to get a notification from system when
>> particular object become garbage, so an additional processing could be
>> activated, called finalization.
>>
>> How things currently working:
>>
>> Squeak provides a default finalization scheme implemented by
>> WeakRegistry class, where one could register a pair of weak reference
>> and executor. Its connected with finalization process , which governed
>> by WeakArray class.
>>
>> Upon each GC, the finalization process awakes and sending
>> #finalizeValues to all objects, registered in FinalizationDependents
>> collection.
>> When a WeakRegistry receives this message, it iterates over own
>> WeakIdentityKeyDictionary instance to find a pairs, for which key
>> (weak ref) became nil, and sends #finalize to the value (executor).
>>
>> The problem with such approach, is a linear time consumption spent for
>> scanning all registered key>value pairs.
>> If you have thousands of objects registered in that way, while only
>> few of them could become garbage, the finalization process still will
>> spend a fair amount of time, trying to determine which ones became
>> garbage.
>>
>> To eliminate this waste, we need to implement a scheme in VM, which
>> can help us to reach only those objects which is died during current
>> GC cycle, leaving rest untouched.
>>
>> Here is what i'd like to propose:
>>
>> - add a special class
>>
>> Object weakSubclass: #WeakReferenceWithNotification
>>        instanceVariableNames: 'list next'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Collections-Weak'
>>
>> so, that VM can check this special kind of weak reference in
>> ObjectMemory>>finalizeReference: method.
>>
>> A 'list' ivar can point to any object, we only need to be sure it
>> having at least one slot to hold a list head:
>>
>> Object subclass: #FinalizationList
>>        instanceVariableNames: 'head'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Collections-Weak'
>>
>> now, all we need is to modify #finalizeReference: method, to do
>> following at the moment of storing nil pointer:
>>
>> " this is not actually the slang code, just to illustrate what it does "
>>
>> (oop class isKindOf: WeakReferenceWithNotification) ifTrue: [
>>  | list |
>>  list := oop at: ListSlotIndex.
>>  oop at: NextSlotIndex put: (list at: HeadSlotIndex).
>>  list at: HeadSlotIndex put: oop.
>> ].
>>
>>
>> What it does, it simply links given oop to the list.
>>
>> And as you may suppose, if we have initial state:
>> - list with head == nil
>> - multiple instances with WeakReferenceWithNotification , all pointing
>> to our list, and having a weak refs,
>>
>> then after GC, if some of the refs become nilled, in a list's head we
>> will have a list of weak references we need to finalize (or do
>> anything else we want to).
>>
>> Then a finalization process in case of WeakRegistry, don't needs to go
>> through all weak references which are registered in itself, instead it
>> could simply do:
>>
>> "suppose we use a subclass "
>> WeakReferenceWithNotification weakSubclass: #WeakReferenceWithExecutor
>>        instanceVariableNames: 'executor'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Collections-Weak'
>> ...
>>
>> ref := list head.
>> ref notNil whileTrue: [
>>   ref executor finalize.
>>   ref := ref next.
>> ].
>> list head: nil.
>>
>> P.S. Me hopes ;) , that i successfully shown that the actual changes
>> to VM is quite simple and minimal. But effects of such change could
>> have a major impact on a finalization scheme performance.
>>
>>
>> --
>> Best regards,
>> Igor Stasenko AKA sig.
>
>
>



--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Andreas.Raab
In reply to this post by Igor Stasenko
 
This is actually quite similar to what I thought about for a more robust
finalization solution. My thought was that you could just use linked
lists (which have operations that are already supported by the VM for
process management) and then make a weak subclass of Link that holds the
object to be watched plus the finalizer for the object. When the VM
traces such a link it puts it onto a VM-level finalization list and
signals the finalization semaphore. The finalization process then pulls
the entries from the list and calls the finalize method on it.

Cheers,
   - Andreas

Igor Stasenko wrote:

>  
> In recent topic on Seaside list, someone has asked, why Seasider's
> abandon the idea of using weak references (and use finalization
> technique when they die).
> The main issue was the slowness of weak finalization process
> implemented in Squeak.
>
> The problem:
> - sometimes developer needs to get a notification from system when
> particular object become garbage, so an additional processing could be
> activated, called finalization.
>
> How things currently working:
>
> Squeak provides a default finalization scheme implemented by
> WeakRegistry class, where one could register a pair of weak reference
> and executor. Its connected with finalization process , which governed
> by WeakArray class.
>
> Upon each GC, the finalization process awakes and sending
> #finalizeValues to all objects, registered in FinalizationDependents
> collection.
> When a WeakRegistry receives this message, it iterates over own
> WeakIdentityKeyDictionary instance to find a pairs, for which key
> (weak ref) became nil, and sends #finalize to the value (executor).
>
> The problem with such approach, is a linear time consumption spent for
> scanning all registered key>value pairs.
> If you have thousands of objects registered in that way, while only
> few of them could become garbage, the finalization process still will
> spend a fair amount of time, trying to determine which ones became
> garbage.
>
> To eliminate this waste, we need to implement a scheme in VM, which
> can help us to reach only those objects which is died during current
> GC cycle, leaving rest untouched.
>
> Here is what i'd like to propose:
>
> - add a special class
>
> Object weakSubclass: #WeakReferenceWithNotification
> instanceVariableNames: 'list next'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Collections-Weak'
>
> so, that VM can check this special kind of weak reference in
> ObjectMemory>>finalizeReference: method.
>
> A 'list' ivar can point to any object, we only need to be sure it
> having at least one slot to hold a list head:
>
> Object subclass: #FinalizationList
> instanceVariableNames: 'head'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Collections-Weak'
>
> now, all we need is to modify #finalizeReference: method, to do
> following at the moment of storing nil pointer:
>
> " this is not actually the slang code, just to illustrate what it does "
>
> (oop class isKindOf: WeakReferenceWithNotification) ifTrue: [
>   | list |
>   list := oop at: ListSlotIndex.
>   oop at: NextSlotIndex put: (list at: HeadSlotIndex).
>   list at: HeadSlotIndex put: oop.
> ].
>
>
> What it does, it simply links given oop to the list.
>
> And as you may suppose, if we have initial state:
> - list with head == nil
> - multiple instances with WeakReferenceWithNotification , all pointing
> to our list, and having a weak refs,
>
> then after GC, if some of the refs become nilled, in a list's head we
> will have a list of weak references we need to finalize (or do
> anything else we want to).
>
> Then a finalization process in case of WeakRegistry, don't needs to go
> through all weak references which are registered in itself, instead it
> could simply do:
>
> "suppose we use a subclass "
> WeakReferenceWithNotification weakSubclass: #WeakReferenceWithExecutor
> instanceVariableNames: 'executor'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Collections-Weak'
> ...
>
> ref := list head.
> ref notNil whileTrue: [
>    ref executor finalize.
>    ref := ref next.
> ].
> list head: nil.
>
> P.S. Me hopes ;) , that i successfully shown that the actual changes
> to VM is quite simple and minimal. But effects of such change could
> have a major impact on a finalization scheme performance.
>
>
Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Igor Stasenko

2009/4/23 Andreas Raab <[hidden email]>:

>
> This is actually quite similar to what I thought about for a more robust
> finalization solution. My thought was that you could just use linked lists
> (which have operations that are already supported by the VM for process
> management) and then make a weak subclass of Link that holds the object to
> be watched plus the finalizer for the object. When the VM traces such a link
> it puts it onto a VM-level finalization list and signals the finalization
> semaphore. The finalization process then pulls the entries from the list and
> calls the finalize method on it.
>

Yes, quite similar. I thought about using a globally registered list
(in special objects table), where VM could stockpile all weak refs
pending for finalization.
But then i thought, that decentralized version of it can be even
better - a developers could organize own list(s) and free choose own
criteria, like at which point they need to visit own list(s) and flush
them.

> Cheers,
>  - Andreas
>
> Igor Stasenko wrote:
>>
>>  In recent topic on Seaside list, someone has asked, why Seasider's
>> abandon the idea of using weak references (and use finalization
>> technique when they die).
>> The main issue was the slowness of weak finalization process
>> implemented in Squeak.
>>
>> The problem:
>> - sometimes developer needs to get a notification from system when
>> particular object become garbage, so an additional processing could be
>> activated, called finalization.
>>
>> How things currently working:
>>
>> Squeak provides a default finalization scheme implemented by
>> WeakRegistry class, where one could register a pair of weak reference
>> and executor. Its connected with finalization process , which governed
>> by WeakArray class.
>>
>> Upon each GC, the finalization process awakes and sending
>> #finalizeValues to all objects, registered in FinalizationDependents
>> collection.
>> When a WeakRegistry receives this message, it iterates over own
>> WeakIdentityKeyDictionary instance to find a pairs, for which key
>> (weak ref) became nil, and sends #finalize to the value (executor).
>>
>> The problem with such approach, is a linear time consumption spent for
>> scanning all registered key>value pairs.
>> If you have thousands of objects registered in that way, while only
>> few of them could become garbage, the finalization process still will
>> spend a fair amount of time, trying to determine which ones became
>> garbage.
>>
>> To eliminate this waste, we need to implement a scheme in VM, which
>> can help us to reach only those objects which is died during current
>> GC cycle, leaving rest untouched.
>>
>> Here is what i'd like to propose:
>>
>> - add a special class
>>
>> Object weakSubclass: #WeakReferenceWithNotification
>>        instanceVariableNames: 'list next'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Collections-Weak'
>>
>> so, that VM can check this special kind of weak reference in
>> ObjectMemory>>finalizeReference: method.
>>
>> A 'list' ivar can point to any object, we only need to be sure it
>> having at least one slot to hold a list head:
>>
>> Object subclass: #FinalizationList
>>        instanceVariableNames: 'head'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Collections-Weak'
>>
>> now, all we need is to modify #finalizeReference: method, to do
>> following at the moment of storing nil pointer:
>>
>> " this is not actually the slang code, just to illustrate what it does "
>>
>> (oop class isKindOf: WeakReferenceWithNotification) ifTrue: [
>>  | list |
>>  list := oop at: ListSlotIndex.
>>  oop at: NextSlotIndex put: (list at: HeadSlotIndex).
>>  list at: HeadSlotIndex put: oop.
>> ].
>>
>>
>> What it does, it simply links given oop to the list.
>>
>> And as you may suppose, if we have initial state:
>> - list with head == nil
>> - multiple instances with WeakReferenceWithNotification , all pointing
>> to our list, and having a weak refs,
>>
>> then after GC, if some of the refs become nilled, in a list's head we
>> will have a list of weak references we need to finalize (or do
>> anything else we want to).
>>
>> Then a finalization process in case of WeakRegistry, don't needs to go
>> through all weak references which are registered in itself, instead it
>> could simply do:
>>
>> "suppose we use a subclass "
>> WeakReferenceWithNotification weakSubclass: #WeakReferenceWithExecutor
>>        instanceVariableNames: 'executor'
>>        classVariableNames: ''
>>        poolDictionaries: ''
>>        category: 'Collections-Weak'
>> ...
>>
>> ref := list head.
>> ref notNil whileTrue: [
>>   ref executor finalize.
>>   ref := ref next.
>> ].
>> list head: nil.
>>
>> P.S. Me hopes ;) , that i successfully shown that the actual changes
>> to VM is quite simple and minimal. But effects of such change could
>> have a major impact on a finalization scheme performance.
>>
>>
>



--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Igor Stasenko
In reply to this post by johnmci

2009/4/23 John M McIntosh <[hidden email]>:

>
> Well there was the Ephemerons stuff you can add too
>
> http://map.squeak.org/package/fe2a35f5-3f97-431e-8596-58e810aa3c72
>
> ian had a version he pushed out a couple of years, back which was retracted
> because it
> caused VM crashs, but later was proven to be an off by 1 C/slang coding
> error.
> But no-one I think had the courage to inflict back onto the community.
>
>
> Er so how would compatibilty work with this new way given an image which may
> or may not include image side companion code.
>

I presume this is a question?
If image don't have a class in special objects array, which should
represent a class for such kind of weak references, then it will never
get into that code, because
(oop class isKindOf: nil "== WeakReferenceWithNotification ") will
always be false.
So, VM+image will behave exactly as in older versions of VM.

>
> --
> ===========================================================================
> John M. McIntosh <[hidden email]>
> Corporate Smalltalk Consulting Ltd.  http://www.smalltalkconsulting.com
> ===========================================================================
>
>
>
>



--
Best regards,
Igor Stasenko AKA sig.
Reply | Threaded
Open this post in threaded view
|

Re: An idea about better finalization support

Igor Stasenko

2009/4/23 Igor Stasenko <[hidden email]>:

> 2009/4/23 John M McIntosh <[hidden email]>:
>>
>> Well there was the Ephemerons stuff you can add too
>>
>> http://map.squeak.org/package/fe2a35f5-3f97-431e-8596-58e810aa3c72
>>
>> ian had a version he pushed out a couple of years, back which was retracted
>> because it
>> caused VM crashs, but later was proven to be an off by 1 C/slang coding
>> error.
>> But no-one I think had the courage to inflict back onto the community.
>>
>>
>> Er so how would compatibilty work with this new way given an image which may
>> or may not include image side companion code.
>>
>
> I presume this is a question?
> If image don't have a class in special objects array, which should
> represent a class for such kind of weak references, then it will never
> get into that code, because
> (oop class isKindOf: nil "== WeakReferenceWithNotification ") will
> always be false.
> So, VM+image will behave exactly as in older versions of VM.
>

Things not so bright, if you run an image which expecting new
semantics on VMs which don't supports it.
Then it could use a fallback version of code (and handle things in a
way, how it currently done). It then will need to check if new
capability is available or not at image startup phase.

>>
>> --
>> ===========================================================================
>> John M. McIntosh <[hidden email]>
>> Corporate Smalltalk Consulting Ltd.  http://www.smalltalkconsulting.com
>> ===========================================================================
>
> --
> Best regards,
> Igor Stasenko AKA sig.
>



--
Best regards,
Igor Stasenko AKA sig.