blob: 5a709112387117f59379dd6fa9c6c5b7a2cfe14e [file] [view]
Proton memory mangement
=======================
Proton is a collection of libraries in different programming langauges. Some of
the libraries (e.g. C and Java) are written directly in those langauges. Some
(e.g. python and ruby) are "bindings", native libraries that that internally use
the C library. All the libraries provide a "native" memory management
experience, you don't need to know anything about C or the proton C library to
use them.
If you only program in java, python, ruby or a similar garbage-collected
language then you may be wondering "what is *memory management*?" Don't worry,
the proton library takes care of it. However, as usual, you are responsible for
some non-memory *resource* management. For example if you fail to `close()` a
proton connection there won't be memory leaks but the remote end will get a rude
"connection failed" error instead of an orderly connection close.
If you are a C programmer, you are probably rolling your eyes and wondering why
the kids these days expect their memory to be managed for them. Don't worry, the
C API offers the standard C experience: you must call `pn_X_free` on a `pn_X_t*`
when the time is right and you must know when a `pn_X_t*` can become invalid as
a side effect of some other call. Read the doc, learn the rules and get it
right. You are a C programmer!
If you are a modern C++11 programmer and always use `shared_ptr` and
`unique_ptr` to write leak-safe code, then the C++ binding is leak-safe. If you
are stuck on C++03, the binding supports `boost::shared_ptr` and
`boost::intrusive_ptr`. If you cannot even use boost, the binding provides a
simple (type safe, intrusive) smart pointer of its own which can help you.
If you are a Go programmer, you know that Go takes care of *Go-allocated* memory
but you are responsible other resource cleanup, e.g. closing connections. Go
does not have object-scoped cleanup (or indeed objects) but as a crafty Go
programmer you know that function-scoped cleanup is all you really need, and
`defer` is your friend. The Go binding internally starts goroutines and
allocates memory that is not tracked by Go, so proper cleanup is important (but
you knew that.)
Proton reference counting in C
------------------------------
Internally, the proton C library uses reference counting, and you can
*optionally* use it in your code. You should choose *either* reference counting
*or* `pn_X_free` in your code, *not both*. It might work, but it is the sort of
Bad Idea that might break your code in the future and will hurt your head in the
present. `pn_X_free` is all you really need to write an AMQP application in C.
However, proton is designed to be embedded and integrated. If you are
integrating proton with a new programming language, or some other kind of
framwork, reference counts may be useful. If your integration target has some
form of automatic clean-up *and* some way for you to hook into it (finalizers,
destructors or the like) then reference counts may help (e.g. python, ruby and
C++). As a counter-example the Go langauge *is* garbage collected but does *not*
have finalizers, and the Go binding does *not* use reference counts, it is
written like a "normal" C application with `pn_X_free`.
If you are mixing your own C code with code using a reference-counted proton
binding (e.g. C++ or python) then you may need to at least be aware of reference
counting.
You can even use reference counts in plain C code if you find that helpful (I
don't see how it would be but you never know.)
The proton C API has standard reference counting rules (but see [1] below)
- A pointer *returned* by a `pn_` function is either *borrowed* by the caller,
or the caller *owns* a reference (the API doc says which.)
- The owner of a reference must call `pn_decref()` exactly once to
release it.
- To keep a borrowed pointer, call `pn_incref()`. This adds a new
reference, which you now own.
- A pointer *passed* to a `pn_` function has no change of ownership. If you
owned a reference you still do, if you didn't you still don't.
- An object is never freed while there are still references to it.
- An object is freed when all references to it are released.
A *borrowed* pointer is valid within some scope (typically the scope of an event
handling function) but beyond that scope you cannot assume it is valid unless
you make a new reference with `pn_incref`. The API documentation for the
function that returned the pointer should tell you what the scope is.
There are "container" relationships in proton: e.g. a connection contains
sessions, a session contains links. Containers hold a reference to their
contents. Freeing a container *releases* that reference. For example freeing a
connection releases its sessions.
If you don't use reference counts, then freeing a container *frees* the contents
in traditional C style. However if you add a reference to a contained object it
will *not* be freed till you release your reference, even if all references to
container are released [1]. This is useful for bindings to langauges with
"finalizers" or "destructors". You can use reference counts to "pin" proton C
objects in memory for as long as any binding object needs them.
For example: if you call `pn_message()` then you *own* a reference to the
newly-created `pn_message_t` and you must call `pn_decref` when you are
done [2]. If you call `pn_event_link()` in an event handler then you get a
*borrowed* reference to the link. You can use it in the scope of the event
handler, but if you want to save it for later you must call `pn_incref`
to add a reference and of course call `pn_decref` when you are done with
that reference.
[1] *Internally* the proton library plays tricks with reference counts to
implement 'weak' pointers and manage circular containment relationships. You do
*not* need to understand this to use proton, even if you are writing bindings or
doing funky mixed-language development. However if you are working on the
implementation of the proton C library itself you may need to learn more, ask on
proton@qpid.apache.org.
[2] Actually if you call `pn_message()` then you must *either* call
`pn_decref()` *or* `pn_message_free()`, definitely not both. It is
possible to mix reference couting and 'free' style memory management in the same
codebase (`free` is sort-of an alias for `decref`) but it is probably not a good
idea.