Destructors, a proposal of sorts

652 views
Skip to first unread message

mschwartz

unread,
Jul 10, 2012, 9:33:52 AM7/10/12
to v8-u...@googlegroups.com
I was reading this great commentary on V8:

I think most of it still applies.

JavaScript doesn't have a concept of destructors like many OO languages, but in a server-side context, there's a real need for them.  Garbage collection in JavaScript is done behind the scenes on objects that are no longer referenced.  When would you call an object's destructor: 1) when it is no longer referenced, or 2) when it is garbage collected?

I suggest that if we could have some "destructor" support in v8, that it be the former (no longer referenced).  It is critical in server-side environments that any (external) resources can be freed as soon as possible.  For example, there are a limited number of file descriptors (fds) available to a Unix/Linux system, and if they're all tied up (open) in some JS object that's been dereferenced, the server can run out of them.  If you deal with fds in wrapped C++ classes made as weak references, those fds can be tied up for a long long time, until v8 maybe decides to GC and call the weak callback.  But if there were a mechanism to register a callback to be called when an object is no longer referenced, the fds could be closed and released to the OS/application for reuse long before V8 maybe decides to garbage collect.

Generally, application developers pretty much know when they're effectively doing a C++ style delete of a JS object (by dereferencing it), so they could be forced to call an agreed upon method, say destroy(), at that time.  But I don't find that so elegant, and it's error prone and can lead to memory/resource leaks that are hard to track down.

I note that JavaScript 1.8.5 provides this:

It leads me to ponder a similar mechanism that might be trivially implemented by the v8 team to help us server-side developers out.  I mean, there are means to define getters and setter and with Harmony there are proxies.  Basically, a lot of functionality that was restricted to C++ code (like MakeWeak) are being exported to JS.  

It's time to provide a mechanism to allow us to define a MakeWeak callback in JS from JS, and ultimately to also implement a destructor type function when an object becomes fully dereferenced.

Am I missing something that's already there?

Andreas Rossberg

unread,
Jul 10, 2012, 9:57:54 AM7/10/12
to v8-u...@googlegroups.com
On 10 July 2012 15:33, mschwartz <myk...@gmail.com> wrote:
JavaScript doesn't have a concept of destructors like many OO languages,

I think "most OO languages" is an exaggeration. Rather, it is an artifact of the few (though popular) OO-languages with manual memory management.

 
but in a server-side context, there's a real need for them.  Garbage collection in JavaScript is done behind the scenes on objects that are no longer referenced.  When would you call an object's destructor: 1) when it is no longer referenced, or 2) when it is garbage collected?

In practice, there is no difference between (1) and (2), because you cannot generally find out that something is no longer referenced except by doing (most of the work of) a garbage collection. At least not in a language with first-class functions and objects.

 
I suggest that if we could have some "destructor" support in v8, that it be the former (no longer referenced).  It is critical in server-side environments that any (external) resources can be freed as soon as possible.  For example, there are a limited number of file descriptors (fds) available to a Unix/Linux system, and if they're all tied up (open) in some JS object that's been dereferenced, the server can run out of them.  If you deal with fds in wrapped C++ classes made as weak references, those fds can be tied up for a long long time, until v8 maybe decides to GC and call the weak callback.  But if there were a mechanism to register a callback to be called when an object is no longer referenced, the fds could be closed and released to the OS/application for reuse long before V8 maybe decides to garbage collect.

You are talking about finalization. It is folklore that using finalization for managing resources other than memory (especially limited resources like OS handles) is almost always a bad idea, because garbage collection is far too non-deterministic for that. You will have no guarantee that finalizers are invoked in a timely manner, because the GC cannot tell.
Maybe I misunderstand, but Object.defineProperty is perfectly standard ES5 functionality, and fully supported by V8. Where do you see the connection to finalization?
 
/Andreas

Vyacheslav Egorov

unread,
Jul 10, 2012, 10:04:00 AM7/10/12
to v8-u...@googlegroups.com
First of all for an arbitrary object in a garbage collected
environment you can't get an instant notification when objects is no
longer referenced. GC does not know that until it runs.

If you are fully controlling the way objects are produced and consumed
you can implement your own memory management (e.g. reference counting
based scheme) in pure JS (but it will be error prone).

That said if you are doing server side JS you can easily expose
MakeWeak to your code. Some node.js modules do that.

--
Vyacheslav Egorov
> --
> v8-users mailing list
> v8-u...@googlegroups.com
> http://groups.google.com/group/v8-users

mschwartz

unread,
Jul 10, 2012, 10:26:31 AM7/10/12
to v8-u...@googlegroups.com
The point I'm trying to make is that v8 obviously has a need for MakeWeak() in C++ land, and for the reasons it exists there, the same reasons exist in JavaScript code.

JavaScript proper doesn't provide for a finalize() kind of mechanism like Java does (I'm not particularly a Java fan), but Java saw the need for implementing such a thing.  

I've been fed the line that garbage collection, as done in languages like C#, Java, JavaScript, et al, was meant to free the programmer from making certain mistakes.  The answer that we should manually call a destroy() type function because we know it's a good time to do so suggests we should call delete (operator) as well for every object we call new (operator).

I think we're in 100% agreement that finalization time is a poor time to manage resource deallocation.  This is the point of my suggestion that there be some mechanism to register a destructor with the JS engine for the object that is called when completely dereferenced.  

Sometimes you can express things in JavaScript that make calling a destroy() function impossible:

var binary = new BinaryThing('abc').concat(new BinaryThing('def'));

The 'def' one is created and dereferenced without a means to call its destroy() method.  Assume BinaryThing is written in JavaScript, but makes calls to C++ code that allocates system resources.

The ECMAScript 1.8.5 method I referenced is an example of how the language (or environment) has evolved to address things lacking in previous versions (such as the ability to protect properties from deletion).

Cheers

Joshua Bell

unread,
Jul 10, 2012, 12:52:03 PM7/10/12
to v8-u...@googlegroups.com
On Tue, Jul 10, 2012 at 7:26 AM, mschwartz <myk...@gmail.com> wrote:
I've been fed the line that garbage collection, as done in languages like C#, Java, JavaScript, et al, was meant to free the programmer from making certain mistakes.  The answer that we should manually call a destroy() type function because we know it's a good time to do so suggests we should call delete (operator) as well for every object we call new (operator).

FWIW, the using (expression) { block } form from C# is IMHO a very nice compromise:

using (Resource r = new Resource(blah))  {
   // do stuff
}

... where Resource is something that implements IDisposable. This is basically just sugar for:

try {
  Resource r = new Resource(blah);
  // do stuff
} finally {
  if (r != null) r.Dispose();
}


It doesn't try and magically stop you from retaining r beyond the block or trying to use it, but it does take an extremely common pattern and real world need and both reduce the amount of typing and leave the code quite readable.

The ECMAScript 1.8.5 method I referenced is an example of how the language (or environment) has evolved to address things lacking in previous versions (such as the ability to protect properties from deletion).

You may want to talk to the es-discuss crew https://mail.mozilla.org/listinfo/es-discuss/ about their thoughts for this sort of support in the language. That said, they may say "prototype it and we'll see" and since node.js is the most likely consumer that could lead you back here.

Is there a common pattern for resource holding types in node.js that would be amenable to a syntactic addition to the language? Is this something that could be prototyped using a transpiler like CoffeeScript or Traceur?
 
On Tuesday, July 10, 2012 6:57:54 AM UTC-7, Andreas Rossberg wrote:
On 10 July 2012 15:33, mschwartz <myk...@gmail.com> wrote:
JavaScript doesn't have a concept of destructors like many OO languages,

I think "most OO languages" is an exaggeration. Rather, it is an artifact of the few (though popular) OO-languages with manual memory management.

 
but in a server-side context, there's a real need for them.  Garbage collection in JavaScript is done behind the scenes on objects that are no longer referenced.  When would you call an object's destructor: 1) when it is no longer referenced, or 2) when it is garbage collected?

In practice, there is no difference between (1) and (2), because you cannot generally find out that something is no longer referenced except by doing (most of the work of) a garbage collection. At least not in a language with first-class functions and objects.

 
I suggest that if we could have some "destructor" support in v8, that it be the former (no longer referenced).  It is critical in server-side environments that any (external) resources can be freed as soon as possible.  For example, there are a limited number of file descriptors (fds) available to a Unix/Linux system, and if they're all tied up (open) in some JS object that's been dereferenced, the server can run out of them.  If you deal with fds in wrapped C++ classes made as weak references, those fds can be tied up for a long long time, until v8 maybe decides to GC and call the weak callback.  But if there were a mechanism to register a callback to be called when an object is no longer referenced, the fds could be closed and released to the OS/application for reuse long before V8 maybe decides to garbage collect.

You are talking about finalization. It is folklore that using finalization for managing resources other than memory (especially limited resources like OS handles) is almost always a bad idea, because garbage collection is far too non-deterministic for that. You will have no guarantee that finalizers are invoked in a timely manner, because the GC cannot tell.
 

I note that JavaScript 1.8.5 provides this:

Maybe I misunderstand, but Object.defineProperty is perfectly standard ES5 functionality, and fully supported by V8. Where do you see the connection to finalization?
 
/Andreas

Sven Panne

unread,
Jul 11, 2012, 2:44:02 AM7/11/12
to v8-u...@googlegroups.com
I suggest reading Boehm's classic paper "Destructors, Finalizers and Synchronization" (http://www.hpl.hp.com/techreports/2002/HPL-2002-335.pdf, slides at http://www.hpl.hp.com/personal/Hans_Boehm/popl03/slides.pdf) before starting yet another finalizer thread (pun intended ;-). The problem is that the issues regarding finalizers are commonly misunderstood, which includes the people who wrote the initial Java spec. I am 99% sure that I read in some interview with James Gosling or Bill Joy that they even regret having finalizers in the spec now, kudos to anybody who can dig up an URL for this.

Regarding C#'s 'using': In a language with first class functions, having a special syntactic construct is not even necessary, see e.g. 'bracket' in Haskell (http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.5.1.0/Control-Exception.html#v:bracket). So if you want to have tight control over the lifetime of your external objects/resources, wrap your usage patterns into higher-order functions and enjoy... :-)

Cheers,
   S.

Andreas Rossberg

unread,
Jul 11, 2012, 4:41:01 AM7/11/12
to v8-u...@googlegroups.com
On 10 July 2012 16:26, mschwartz <myk...@gmail.com> wrote:
I think we're in 100% agreement that finalization time is a poor time to manage resource deallocation.  This is the point of my suggestion that there be some mechanism to register a destructor with the JS engine for the object that is called when completely dereferenced. 

Well, again, how is the runtime supposed to find out, except by GC? That's how MakeWeak works, too. It is finalization. You can't do better, unless you buy into reference counting (with all its problems and overhead).

 
The ECMAScript 1.8.5 method I referenced is an example of how the language (or environment) has evolved to address things lacking in previous versions (such as the ability to protect properties from deletion).

OK, but I would argue that API design and evolution would be the trivial part of your proposal.

/Andreas

Stephan Beal

unread,
Jul 11, 2012, 7:03:47 AM7/11/12
to v8-u...@googlegroups.com
On Tue, Jul 10, 2012 at 4:26 PM, mschwartz <myk...@gmail.com> wrote:
meant to free the programmer from making certain mistakes.  The answer that we should manually call a destroy() type function because we know it's a good time to do so suggests we should call delete (operator) as well for every object we call new (operator).

FWIW: for ALL of my bound natives i bind a member "destroy" function (and typically use the try/finally approach to ensuring that they are cleaned up). The exact name of the function is class-dependent but the implementation is a template which does:

a) unbinds the native pointer from the JS Object.
b) calls the registered weak destruction callback (which almost always calls (delete theNative)).

Afterwards, calling theBoundJSObject.anyMemberFuncWhichNeedsTheNative() will cause a JS exception to be thrown ("cannot find native 'this' pointer"), as opposed to crashing or stepping on a now-reallocated address which might now even belong to a different class of object.


--
----- stephan beal
http://wanderinghorse.net/home/stephan/

mschwartz

unread,
Jul 11, 2012, 8:32:57 AM7/11/12
to v8-u...@googlegroups.com
You left out the best part of my last post.  I'll try again, but this time with a more concrete example.

I implemented Canvas server-side.  It consists of about 7000 lines of C++ code wrapping libcairo (the libcairo code is directly callable from JS as well) plus implementation of Canvas, CanvasDrawingContext2d,  CanvasPathMethods, and Image in JavaScript.  There are a number of methods and methodologies involved in using Canvas that create all kinds of Cairo native objects, as well as the Images which are also native.  I don't see any way around the way the API works.  Sure, I implemented (as Stephan Beal suggests) destroy() methods for all those things, but that's not part of the Canvas spec.

So what's the point?  You can run code written for the client to generate images using Canvas on the server side.  In production, SilkJS users (some fortune 100 companies, in fact) use the feature and their demand for it is why I wrote it.  None of that client-side code they're running server-side calls a destroy() function on objects created; destroy() isn't in the spec.

SilkJS' architecture, as an apache style pre-fork server makes it possible to set the # of requests served per child to some low number so the child PROCESSES exit and that is a way to do garbage collection of all these native objects, but in any other context (e.g. NodeJS, a long running Context), a Canvas implementation that runs client-side code verbatim is going to leak memory.

If you're interested in the JS Canvas implementation, it's here:
And the Cairo C++ code is here:

Canvas is just one of many APIs that suffer from this issue.

Maybe server-side support isn't a priority for V8, which is understandable.  The browser is hugely popular and the driver for the development of V8 in the first place.  However, I've taken to shutting down all my Chrome instances at work on Friday on my Ubuntu workstation, or else i show up on Monday to find Chrome has chewed through my 16G of RAM and most of my swap space.

Maybe it is a browser issue as well.

Andreas Rossberg

unread,
Jul 11, 2012, 8:50:06 AM7/11/12
to v8-u...@googlegroups.com
On 11 July 2012 14:32, mschwartz <myk...@gmail.com> wrote:
You left out the best part of my last post.  I'll try again, but this time with a more concrete example.

Yes, there are plenty of use cases that I could imagine. It's a well-known issue, and has been for decades. But unfortunately, that does not change the fundamental problem: there is no magic by which any runtime could efficiently provide the behaviour you want. GC-time finalization is as close as you can get.

/Andreas

Michael Schwartz

unread,
Jul 11, 2012, 9:35:05 AM7/11/12
to v8-u...@googlegroups.com
GC finalization actually works for SilkJS.  In the HTTP server, there are two nested loops.  The outer loop waits for connections, the inner loop handles keep-alive requests.  At the end of the inner loop (e.g. when the connection is about to be closed), I force a GC (or at least try to).

But there still isn't a good way from JS code to register a finalize sort of method.  

The thing is, it really shouldn't matter of implementing such a thing is part of the ECMA specs or anything like that.  I don't know that client-side code would particularly make use of the feature.  Given that I know 100% of the time my server-side code is running in V8, I don't have to worry about the feature being present - it always would be.

Andreas Rossberg

unread,
Jul 11, 2012, 10:38:24 AM7/11/12
to v8-u...@googlegroups.com
On 11 July 2012 15:35, Michael Schwartz <myk...@gmail.com> wrote:
GC finalization actually works for SilkJS.  In the HTTP server, there are two nested loops.  The outer loop waits for connections, the inner loop handles keep-alive requests.  At the end of the inner loop (e.g. when the connection is about to be closed), I force a GC (or at least try to).

Hm, I don't quite follow. If you actually have a specific point where you know that you want to dispose a resource, why is it impossible to dispose it directly at that point? Or if there are many of them, you could maintain a set/map of them.

In any case, before you conclude that finalization is the answer to your problems, let me +1 Sven's recommendation on reading Boehm's paper on the subject. Finalization is pretty much an anti-pattern. There are some rare, low-level use cases, but usually it creates more problems than it solves. That's why we only provide it in the C++ API.

Also, I should mention that forcing a GC manually is highly discouraged. That causes a full (major, non-incremental) collection, which generally is a very costly operation that can cause significant pause time, and basically defeats most of the goodness built into a modern GC.

/Andreas

Michael Schwartz

unread,
Jul 11, 2012, 11:02:36 AM7/11/12
to v8-u...@googlegroups.com
Here's a pattern, using Canvas as an example:

function foo() {
  var c = new Canvas();
  var ctx = c.getContext('2d');
  var grad = ctx.createLinearGradient(0,0, 10,10);
  // do something / render
}

There's now a native cairo_surface_t created (new Canvas).  The c variable has a this.surface referencing the native surface object.
There's now a native cairo_context_t created (c.getContext). The ctx variable has a this.ctx referencing the native context object.
There's now a native cairo_pattern_t created (c.createLinearGradient).  The grad variable has a this.pattern referencing the native pattern object.

All three need to be cleaned up at some point.

There are no "destroy" (destructor) methods defined in the W3C spec for Canvas, Context, Gradient, etc.  Nobody is going to call them (they don't client-side), unless writing non-portable SilkJS specific code.  And ideally, the code should be portable between client and server - that's one of the best features of JavaScript running on both sides.

I'm fully aware of the issues with finalize and reference counting based GC, etc.   Stephan made his rant about garbage collection and destructor issues in that link I posted in my first message.  Things haven't changed.  He and I have been and still are developing complex API for server-side, and this is an issue we both face.  Surely every API designer faces the same problem.  Assist from V8 in addressing the problem would benefit a wide audience.

As for my choice to force GC:


2) V8′s idle garbage collection is disabled via “–nouse-idle-notification”

This was critical, as the server pre-allocates over 2 million JS Objects for the network topology. If you don’t disable idle garbage collection, you’ll see a full second of delay every few seconds, which would be an intolerable bottleneck to scalability and responsiveness. The delay appears to be caused by the garbage collector traversing this list of objects, even though none of them are actually candidates for garbage collection.

In my case, I know when it is a good time to force a GC.  It is being done in process that is about to block in accept().  If it ties up a CPU core for a bit, it is not going to stop the other processes from running, nor is the GC going to pause the server in action.

(The above is one of numerous WWW pages I've read about scaling NodeJS, garbage collection pauses during benchmarks, etc.)


Vyacheslav Egorov

unread,
Jul 11, 2012, 12:08:45 PM7/11/12
to v8-u...@googlegroups.com
Actually finalization in reference counting GCs is much more
predictable than in GCs that mark through the heap.

Contrary to want you might think --nouse-idle-notification does not
disable automatic GC in V8. What it does is tells V8 not to perform GC
actions (be it advance the sweeper, incremental marker or do a full
GC) in response to IdleNotifications that embedder (node.js in this
case) sends to V8.

If V8 sees fit (e.g. on allocation failure) it _will_ perform it and
you can't disable that.

[also running through all objects is kinda how GC works, though it
should be done in increments]

--
Vyacheslav Egorov

Stephan Beal

unread,
Jul 11, 2012, 1:02:55 PM7/11/12
to v8-u...@googlegroups.com
On Wed, Jul 11, 2012 at 4:38 PM, Andreas Rossberg <ross...@google.com> wrote:
In any case, before you conclude that finalization is the answer to your problems, let me +1 Sven's recommendation on reading Boehm's paper on the subject. Finalization is pretty much an anti-pattern. There are some rare, low-level use cases, but usually it creates more problems than it solves. That's why we only provide it in the C++ API.

Just to beat a long-dead horse for a moment...

Quoting Boem's paper:

Page 1: "We argue that although finalization is rarely needed, it is often critical when it is needed. In these cases, its absence would render the garbage collector useless."

"Rarely needed" is, in my (not inconsiderable) experience, 100% wrong for JS bindings. The vast majority of the interesting client-side NATIVE types which we want to bind to JS require resource acquisition and a large percentage of them require well-defined finalization/destruction in order to guaranty proper behaviour. 

Examples, just off the top of my head:

- File handles must be close()d in order to ensure that they are flushed. Yes, the OS closes them when the process exit()s, but AFAIK they are not automatically flush()ed.

- Database drivers, statement handles, and query cursors all have very specific cleanup requirements, and failing to properly close() a db handle "could" (though admittedly "shouldn't") lead to undefined behaviour e.g. db corruption.

- Anything which heavily relies on parent/child relationships, UI toolkits being a prime example. Getting those working properly in JS can be a real PITA because the child/parent relationships must be tracked in both JS and native space in order to keep all the objects around for their proper lifetimes. (When i first implements an ncurses binding for JS i quickly found i had to make JS aware of all child window references to keep them from being GC'd out from under the parent window's nose.)


We (client-side users of scripting engines) don't bind trivial classes to native implementations. Such impls are done in JS because it's 100x times easier to do so (e.g. the canonical "Point" class example). When we bind natives its because we need native functionality which we cannot get at from JS, and the vast majority of such functionality requires access to "interesting" native resources with arbitrarily complex compositions and cleanup requirements. i don't know of a single commonly-used C/C++ library which does _not_ require that clients clean up resources it asked the library to allocate for them.


Section 3.3, bullet point #1: "There is usually not much of a point in writing a finalizer that touched only the object being finalized, since such object updates wouldn't normally be observable."

i understand that this assertion "can be true", but it assumes that the statement from Page 1 is true, which it is absolutely not for the generic case when binding native C/C++ types to JS.

To be clear: i'm not picking no v8 here. In time since i wrote the original gripes which Michael S. linked to in the top post of this thread, i have since come to understand much more about the difficulties of finalization ordering and whatnot, and have a much deeper appreciation for the nature of the problem. Nonetheless, assertions like Boem's quotes above, in my opinion, simply hand-wave away a _majority_ of use cases as "not useful" or "rarely seen," when in fact the problem is that they are just damned difficult to solve. Obviously Boem is the top of his field, and i don't discount his brilliance in this subject matter, but hand-waving away such problems as being "infrequent" does him no credit.

-- 

Stephan Beal

unread,
Jul 11, 2012, 1:04:13 PM7/11/12
to v8-u...@googlegroups.com
On Wed, Jul 11, 2012 at 7:02 PM, Stephan Beal <sgb...@googlemail.com> wrote:
Section 3.3, bullet point #1: "There is usually not much of a point in writing a finalizer that touched only the object being finalized, since

PS: the typos are mine, not Boem's - my PDF reader won't let me copy/paste :/.

Sven Panne

unread,
Jul 12, 2012, 2:43:29 AM7/12/12
to v8-u...@googlegroups.com
The beating continues... ;-)

Regarding Boehm's "finalization is rarely needed": I think statement generally holds, *except* for the case we are discussing, namely writing a binding for a library with explicit allocation/deallocation where the deallocation part is not exposed in the binding. While it is very convenient for the user of such a binding, it simply wipes some complexity of the original library under the carpet, hoping for a magic solution from the binding language/runtime.

When one uses finalizers for this magic solution, one faces another problem, namely the usual tradeoff between latency and throughput: For the vast majority of use cases of a GC, throughput is much, much more important than latency. In theory one could write a basically zero-latency GC (e.g. by doing reference counting on strongly connected components) where finalizers would fire immediately, but the overhead is so huge that no real collector I know of does it this way. So using finalizers in the real world will always involve some kind of delay, and you simply have to live with that.

If the delay is intolerable and you have certain points in your application/library/framework where you know something about the lifetime of the underlying library objects (which seems to be the case for our example), you can deallocate the "dead" ones by hand, using the finalizer calls from the GC only as an additional hint to do this early. Having written several library bindings for myself, I am quite aware of the fact that the additional complexity of doing this in the binding layer is not nice, because it involves some housekeeping of the library objects there, but this is the price of hiding complexity from the user of the binding.

Regarding Boehm's "Section 3.3, bullet point #1:": This assertion is true, because in a library binding there are at least *2* objects involved, in our example the JavaScript canvas object plus the underlying Cairo surface.

Cheers,
   S.

Stephan Beal

unread,
Jul 12, 2012, 3:35:36 AM7/12/12
to v8-u...@googlegroups.com

Sorry for top posting (tablet gmail makes it difficult to do otherwise)...

I should have specied that correct, well-defined behaviour is far, far more important to me than gc-related lags. For me it has never been a question of performance, but of well-defined (non- performance- related) behaviours and returning resources to their owners. Yes, i use finalizers for all of my bound native types, but that is so that they have well-defined behaviour and has 0 to do with speed. Obviously that requirement is out of scope for chromium, which is fine in and of itself but very negatively impacts v8's re-use in other projects because we embedders often have to expend undue amounts of energy shoe-horning finalization into not only our c++ code but also into our docs and users. (Yes, it's our own fault, but without bindings to interesting natives js is not much use.)

--

Deepak Bawa

unread,
Dec 29, 2016, 1:56:59 AM12/29/16
to v8-users
yes i need a destructor as am developing a test application using nodejs and nwjs (nwjs.io). The connection to database is established as i instantiate the class object but when i need to call another method node gives me error as the refference to undefined connection variable . i need destructor to unset or destroy all the member variables as the class terminated.
Reply all
Reply to author
Forward
0 new messages