Definition:
|
integer tid = new_dict(sequence kd_pairs={},
integer pool_only=0)
-- or --
integer tid = new_dict(string dictname="")
|
Description:
|
Creates a new dictionary, so that clashes with other uses of {key,data} pairings can be avoided, and optionally
initialises it with a set of {key,data} pairs, or a diagnostic name.
|
Comments:
|
If kd_pairs is a string then it is treated as a name for the dictionary, which may be useful for diagnostic purposes,
and can be retrieved using dict_name().
Otherwise every element of kd_pairs, if present, should be a sequence of length 2, and said {key,data} gets passed to
setd(), along with the id of the newly created dictionary.
The returned tid is intended to be passed as the last parameter to setd(), getd(),
deld(), destroy_dict(), traverse_dict(),
and similar, taking care not to miss any.
For smaller applicatons, there is a default dictionary, 1, that can be used without ever calling new_dict(), however larger
applications and in particular third party components will obviously benefit from keeping their data private (by the simple
act of not sharing their tid1).
When you have no further use for it, an entire dictionary can be removed by invoking destroy_dict(tid), except for the
default dictionary, 1, as mentioned above, which is special and is the only dictionary that cannot be deleted - attempts
to do so merely empty it and leave it still available for use.
Dictionaries are not thread-safe, for details of that, or if you are curious about the mysterious pool_only
parameter, see the Technicalia drop-down.
|
Disclaimer:
|
1Rogue code that wantonly writes garbage to a tid of, say, 5, when it did not obtain
that 5 from new_dict(), can obviously wreak havoc. Should it ever prove necessary it would probably be fairly easy to
change integer tid to {integer tid, string passkey} just to make it far harder to accidentally replicate a tid.
|
Example 1:
|
integer tid = new_dict({{1,"one"},{2,"two"}})
|
Example 2:
|
-- multithreaded applications need to use locking (see Technicalia)
integer dictcs = init_cs(), tid
enter_cs(dictcs)
tid = new_dict("thread data")
leave_cs(dictcs)
|
See Also:
|
enter_cs,
destroy_dict
|
Technicalia
|
Multiple threads concurrently accessing dictionaries will cause corruption unless protected as shown in Example 2.
If your application is not multithreaded, or only one thread accesses dictionaries, then nothing that follows is of
any concern to you, and if I were you, I’d get out while I still can.
A single critical section protecting all dictionaries is normally sufficient, and in the vast majority of cases there
would be no point making life any more difficult for yourself, however per-dictionary locks are also perfectly viable,
and if only one thread has access to a particular dictionary, then only new_dict() and destroy_dict() may need locking
- but for either of the last two options (ie per-dict and create/destroy-only-locking), then a big enough pool must be
created at the get-go:
If pool_only is greater than 1, the routine adds the specified number of entries to the freelist and returns 0.
Such a call is of course expected to be made before any create_thread() or other
new_dict() calls, and certainly before any destroy_dict() calls - you should verify
the result actually is zero to guard against the latter.
Subsequent invocations of new_dict(pool_only:=1) [presumably from within an appropriate critical section] will yield 0
if the pool is exhausted. In that case, the program should unlock, sleep and retry a bit later. You can actually specify
a diagnostic name and pool_only of 1 (or 0), however passing a string plus pool_only>1 triggers a fatal error.
If in fact you are trying to shut down, but other threads have died or forgotten to destroy_dict(), it is not entirely
unreasonable (after a time) to offer a "create anyway" option - it is not actually very likely, let alone guaranteed
to fail - unless some other thread is beavering away making as many calls to builtins\dict.e routines as it can.
I only mention this because systems which are multithreaded and require many dictionaries are somewhat more
likely to have complex and critical shutdown requirements, and stand to lose much more data if terminated abruptly.
Tip: it costs very little to create a pool of say 500 dicts, and any such overhead would be completely and utterly
dwarfed by trying to create 500 threads. Conversely, limiting the pool size may keep memory useage spikes down and
otherwise help spread out the workload.
Let me reiterate to make all this as clear as possible:
- If the program is not multithreaded, or only one thread accesses dictionaries, no locking required, go away now.
- If multiple threads might access the same dictionary, locks are required around every call.
- In the latter case you can have one lock for all dictionaries or one lock per dictionary (see last two points).
- If a dictionary is only ever accessed by one thread, you can limit locking to new_dict/destroy_dict, but you
must work within a limited-size pool of dictionaries (or lock all calls).
- Likewise any use of per-dictionary locks mandates the use of a limited-size pool of dictionaries.
- The locks around new_dict/destroy_dict calls must always of course be system-wide, not per-dictionary.
|