Deleting individual dynamic objects is a big responsibility, and a serious nuisance as well. Widget pools are designed to help you make your use of dynamic objects much simpler and more reliable.

A widget pool provides a means of treating objects you create as a set. When you delete the pool, all the objects you created in it go away together. The simplest way to group objects using widget pools is to associate all the objects in a single procedure with a widget pool. In fact, the template procedures used by the AppBuilder for windows (and for visual SmartObjects, such as SmartWindows and SmartDataViewers) help you do this by having a CREATE WIDGET-POOL statement in the template’s definitions section:
/* Create an unnamed pool to store all the widgets created
    by this procedure. This is a good default which assures
    that this procedure's triggers and internal procedures
    will execute in this procedure's storage, and that proper
    cleanup will occur on deletion of the procedure. */

CREATE WIDGET-POOL.

This means that, by default, all the dynamic objects you create that go into the window are deleted when the window is closed and its procedure deleted. This includes not just visual objects but dynamic buffers, queries, and so forth.

Remember that a simple CREATE WIDGET-POOL statement creates an unnamed widget pool. This pool automatically goes away when its procedure terminates. This might not always be the behavior you want. In fact, by passing the handles to dynamic objects around, you can easily wind up with a handle whose scope exceeds the lifetime of the dynamic object it points to. In this case, the value of the handle can become invalid.

Here’s a simple example of how this can happen. The h-MakeBuffer.p procedure has an internal procedure called getBuffer that creates a dynamic buffer and returns it to the caller. The CREATE WIDGET-POOL statement puts all such buffers into an unnamed widget pool for that procedure:
/* h-MakeBuffer.p */

CREATE WIDGET-POOL. /* Unnamed widget pool! */

PROCEDURE getBuffer:
  DEFINE INPUT PARAMETER cTable AS CHARACTER NO-UNDO.
  DEFINE OUTPUT PARAMETER hBuffer AS HANDLE NO-UNDO.

  /* The buffer will be allocated to the unnamed widget pool. */
  CREATE BUFFER hBuffer FOR TABLE cTable.
END PROCEDURE.
Another procedure, h-RunMakeBuff.p, runs h-MakeBuffer.p PERSISTENT and then runs getBuffer:
/* h-RunMakeBuff.p */

DEFINE VARIABLE hProc AS HANDLE NO-UNDO.
DEFINE VARIABLE hMyBuf AS HANDLE NO-UNDO.

RUN h-MakeBuffer.p PERSISTENT SET hProc.
RUN getBuffer IN hProc (INPUT "Customer", OUTPUT hMyBuf).

MESSAGE "While MakeBuffer is alive, my buffer is "
  hMyBuf:NAME VIEW-AS ALERT-BOX.
DELETE PROCEDURE hProc.
MESSAGE " After I delete it, I get nasty errors"
  hMyBuf:NAME VIEW-AS ALERT-BOX.
The first message statement, as expected, correctly displays the name of the dynamic buffer that was returned, as shown in the figure.

What happens, though, when the procedure has deleted its instance of h-MakeBuffer.p?

The unnamed widget pool in that instance of h-MakeBuffer.p goes away, and the dynamic buffer that hMyBuf points to goes away with it. But the hMyBuf variable is still very much alive, and in fact the value of the handle that it holds has not changed. However, that handle value does not point to anything anymore, so you get a string of errors, as shown here.

This sequence of errors is the bane of any dynamic programmer’s existence. It is the most likely consequence of failing to make sure that all your dynamic objects live exactly as long as they need to, but no longer.

Use named widget pools

The solution to this is to give names to widget pools that hold objects that might outlive the procedure that created them. In addition to giving the pool a name, you must also define it to be PERSISTENT. You can then create dynamic objects explicitly in that pool:
/* h-MakeBuffer.p */

CREATE WIDGET-POOL "MakeBuffPool" PERSISTENT.

PROCEDURE getBuffer:
  DEFINE INPUT PARAMETER cTable AS CHARACTER NO-UNDO.
  DEFINE OUTPUT PARAMETER hBuffer AS HANDLE NO-UNDO.
  /* The buffer will be allocated to the unnamed widget pool. */
  CREATE BUFFER hBuffer FOR TABLE cTable IN WIDGET-POOL "MakeBuffPool".
END PROCEDURE.
The persistent widget pool becomes another dynamic object that your application has to take responsibility for managing. You must delete it when you are finally done with, or all the objects in it will sit there in memory until your session ends. Here the calling procedure cleans up after it is done using the buffer handle:
/* … end of h-RunMakeBuff.p … */
DELETE PROCEDURE hProc.
MESSAGE " No more errors, the buffer is still"
  hMyBuf:NAME VIEW-AS ALERT-BOX.
DELETE WIDGET-POOL "MakeBuffPool".

When you run the procedure, the errors go away, because the handle is valid until you delete the pool.

Remember that all these steps are important:
  1. You must give the widget pool a name if you want to allocate objects to it specifically or if you want it to outlive the procedure that creates it.
  2. The widget pool name is a character expression. So, if you are using a literal string rather than a variable for the name, you must remember to put it in quotes both where you create it and wherever you reference it. This is different from other CREATE statements, where you normally specify a handle variable as a target for the create and where the dynamic object you create does not really have a name the same way that static objects do.
  3. You must also make a named pool PERSISTENT if you want it to outlive the procedure that creates it.
  4. You must remember to delete the widget pool when you are done using it, just as you delete individual dynamic objects that are not in a specific widget pool when you are done with them.

You might create a named widget pool that was not persistent simply to put different dynamic objects in different pools within a single procedure, so that you could delete one widget pool within the procedure without deleting objects in some other widget pool. In this case, all of the pools that have not been specifically deleted during the execution of the procedure are deleted when the procedure is deleted.

A widget pool cannot be created as PERSISTENT without giving it a name.

When you create a persistent named pool, its name effectively becomes global to the session. Any procedure running anywhere in the session can delete it. You cannot, however, access named widget pools through the SESSION handle as you can with some other kinds of objects, such as windows and procedures.