Skip to content

Commit

Permalink
Merge branch 'raimo/doc-md'
Browse files Browse the repository at this point in the history
* raimo/doc-md:
  Polish documentation
  Remove doc src indentation according to current OTB preferemce
  Polishing documentation
  Move around text to get to the point faster
  Improve ASCII art callback module picture
  Resurrect the OTP 19 `gen_fsm` documentation
  Rephrase description of `{continue,_}`
  Rework documentation after Markdown conversion
  Rework documentation after Markdown conversion
  Rework documentation after Markdown conversion
  Handle callback section titles
  • Loading branch information
RaimoNiskanen committed Jun 19, 2024
2 parents a191635 + 1c79e19 commit 0ff17dc
Show file tree
Hide file tree
Showing 10 changed files with 5,034 additions and 3,031 deletions.
1,264 changes: 718 additions & 546 deletions lib/stdlib/src/gen_event.erl

Large diffs are not rendered by default.

1,007 changes: 930 additions & 77 deletions lib/stdlib/src/gen_fsm.erl

Large diffs are not rendered by default.

1,538 changes: 855 additions & 683 deletions lib/stdlib/src/gen_server.erl

Large diffs are not rendered by default.

2,943 changes: 1,778 additions & 1,165 deletions lib/stdlib/src/gen_statem.erl

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions lib/stdlib/test/erl_internal_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ callbacks(gen_server) ->
{handle_info,2}, {terminate,2}, {code_change,3},
{format_status,1}, {format_status,2}, {handle_continue, 2}];
callbacks(gen_fsm) ->
[{init,1}, {handle_event,3}, {handle_sync_event,4},
[{init,1}, {'StateName',2}, {'StateName',3},
{handle_event,3}, {handle_sync_event,4},
{handle_info,3}, {terminate,3}, {code_change,4},
{format_status,2}];
callbacks(gen_event) ->
Expand All @@ -103,7 +104,8 @@ optional_callbacks(application) ->
optional_callbacks(gen_server) ->
[{handle_info, 2}, {handle_continue, 2}, {terminate, 2}, {code_change, 3}, {format_status, 1}, {format_status, 2}];
optional_callbacks(gen_fsm) ->
[{handle_info, 3}, {terminate, 3}, {code_change, 4}, {format_status, 2}];
[{'StateName', 2}, {'StateName', 3},
{handle_info, 3}, {terminate, 3}, {code_change, 4}, {format_status, 2}];
optional_callbacks(gen_event) ->
[{handle_info, 2}, {terminate, 2}, {code_change, 3}, {format_status, 1}, {format_status, 2}];
optional_callbacks(gen_statem) ->
Expand Down
20 changes: 15 additions & 5 deletions make/ex_doc.exs
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,24 @@ groups_for_docs =
) ++
[Types: &(&1[:kind] == :type)] ++
Enum.map(
Access.get(titles, :function, []),
fn {:function, title} ->
{"#{title}",
Access.get(titles, :callback, []),
fn {:callback, title} ->
{"Callbacks: #{title}",
fn a ->
a[:kind] == :function && String.equivalent?(Access.get(a, :title, ""), title)
a[:kind] == :callback && String.equivalent?(Access.get(a, :title, ""), title)
end}
end
)
) ++
[Callbacks: &(&1[:kind] == :callback)] ++
Enum.map(
Access.get(titles, :function, []),
fn {:function, title} ->
{"#{title}",
fn a ->
a[:kind] == :function && String.equivalent?(Access.get(a, :title, ""), title)
end}
end
)

## Create the correct source url to github
base_url = "https://github.com/" <> System.get_env("BASE_URL", "erlang/otp/blob/master/")
Expand Down
Binary file not shown.
107 changes: 107 additions & 0 deletions system/doc/design_principles/assets/ballpoint-pen.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 53 additions & 45 deletions system/doc/design_principles/gen_server_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@ limitations under the License.
%CopyrightEnd%
-->
# gen_server Behaviour

[](){: #gen_server }
[](){: #gen_server }gen_server Behaviour
========================================

It is recommended to read this section alongside `m:gen_server` in STDLIB.

## Client-Server Principles
Client-Server Principles
------------------------

The client-server model is characterized by a central server and an arbitrary
number of clients. The client-server model is used for resource management
operations, where several different clients want to share a common resource. The
server is responsible for managing this resource.
operations, where several different clients want to share a common resource.
The server is responsible for managing this resource.

[](){: #clientserver }

Expand Down Expand Up @@ -65,7 +66,8 @@ flowchart LR
end
```

## Example
Example
-------

An example of a simple server written in plain Erlang is provided in
[Overview](design_principles.md#ch1). The server can be reimplemented using
Expand Down Expand Up @@ -104,7 +106,8 @@ handle_cast({free, Ch}, Chs) ->

The code is explained in the next sections.

## Starting a Gen_Server
Starting a Gen_Server
---------------------

In the example in the previous section, `gen_server` is started by calling
`ch3:start_link()`:
Expand All @@ -114,11 +117,11 @@ start_link() ->
gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}
```

`start_link/0` calls function `gen_server:start_link/4`. This function spawns and
links to a new process, a `gen_server`.
`start_link/0` calls function `gen_server:start_link/4`. This function
spawns and links to a new process, a `gen_server`.

- The first argument, `{local, ch3}`, specifies the name. The gen_server is then
locally registered as `ch3`.
- The first argument, `{local, ch3}`, specifies the name.
The gen_server is then locally registered as `ch3`.

If the name is omitted, the `gen_server` is not registered. Instead its pid
must be used. The name can also be given as `{global, Name}`, in which case
Expand Down Expand Up @@ -157,7 +160,8 @@ a supervision tree, meaning that it was started by a supervisor. There
is another function, `gen_server:start/4`, to start a standalone
`gen_server` that is not part of a supervision tree.

## Synchronous Requests - Call
Synchronous Requests - Call
---------------------------

The synchronous request `alloc()` is implemented using `gen_server:call/2`:

Expand All @@ -166,14 +170,14 @@ alloc() ->
gen_server:call(ch3, alloc).
```

`ch3` is the name of the `gen_server` and must agree with the name used to start
it. `alloc` is the actual request.
`ch3` is the name of the `gen_server` and must agree with the name
used to start it. `alloc` is the actual request.

The request is made into a message and sent to the `gen_server`. When the
request is received, the `gen_server` calls `handle_call(Request, From, State)`,
which is expected to return a tuple `{reply,Reply,State1}`. `Reply` is the reply
that is to be sent back to the client, and `State1` is a new value for the state
of the `gen_server`.
The request is made into a message and sent to the `gen_server`.
When the request is received, the `gen_server` calls
`handle_call(Request, From, State)`, which is expected to return
a tuple `{reply,Reply,State1}`. `Reply` is the reply that is to be sent back
to the client, and `State1` is a new value for the state of the `gen_server`.

```erlang
handle_call(alloc, _From, Chs) ->
Expand All @@ -185,10 +189,11 @@ In this case, the reply is the allocated channel `Ch` and the new state is the
set of remaining available channels `Chs2`.

Thus, the call `ch3:alloc()` returns the allocated channel `Ch` and the
`gen_server` then waits for new requests, now with an updated list of available
channels.
`gen_server` then waits for new requests, now with an updated list of
available channels.

## Asynchronous Requests - Cast
Asynchronous Requests - Cast
----------------------------

The asynchronous request `free(Ch)` is implemented using `gen_server:cast/2`:

Expand All @@ -199,8 +204,8 @@ free(Ch) ->

`ch3` is the name of the `gen_server`. `{free, Ch}` is the actual request.

The request is made into a message and sent to the `gen_server`. `cast`, and
thus `free`, then returns `ok`.
The request is made into a message and sent to the `gen_server`.
`cast`, and thus `free`, then returns `ok`.

When the request is received, the `gen_server` calls
`handle_cast(Request, State)`, which is expected to return a tuple
Expand All @@ -215,19 +220,20 @@ handle_cast({free, Ch}, Chs) ->
In this case, the new state is the updated list of available channels `Chs2`.
The `gen_server` is now ready for new requests.

## Stopping
Stopping
--------

### In a Supervision Tree

If the `gen_server` is part of a supervision tree, no stop function is needed.
The `gen_server` is automatically terminated by its supervisor. Exactly how this
is done is defined by a [shutdown strategy](sup_princ.md#shutdown) set in the
supervisor.
The `gen_server` is automatically terminated by its supervisor. Exactly how
this is done is defined by a [shutdown strategy](sup_princ.md#shutdown)
set in the supervisor.

If it is necessary to clean up before termination, the shutdown strategy must be
a time-out value and the `gen_server` must be set to trap exit signals in
function `init`. When ordered to shutdown, the `gen_server` then calls the
callback function `terminate(shutdown, State)`:
If it is necessary to clean up before termination, the shutdown strategy
must be a time-out value and the `gen_server` must be set to trap exit signals
in function `init`. When ordered to shutdown, the `gen_server` then calls
the callback function `terminate(shutdown, State)`:

```erlang
init(Args) ->
Expand All @@ -246,8 +252,8 @@ terminate(shutdown, State) ->

### Standalone Gen_Servers

If the `gen_server` is not part of a supervision tree, a stop function can be
useful, for example:
If the `gen_server` is not part of a supervision tree, a stop function
can be useful, for example:

```erlang
...
Expand All @@ -270,17 +276,19 @@ terminate(normal, State) ->
```

The callback function handling the `stop` request returns a tuple
`{stop,normal,State1}`, where `normal` specifies that it is a normal termination
and `State1` is a new value for the state of the `gen_server`. This causes the
`gen_server` to call `terminate(normal, State1)` and then it terminates
gracefully.

## Handling Other Messages

If the `gen_server` is to be able to receive other messages than requests, the
callback function `handle_info(Info, State)` must be implemented to handle them.
Examples of other messages are exit messages, if the `gen_server` is linked to
other processes than the supervisor and it is trapping exit signals.
`{stop,normal,State1}`, where `normal` specifies that it is
a normal termination and `State1` is a new value for the state
of the `gen_server`. This causes the `gen_server` to call
`terminate(normal, State1)` and then it terminates gracefully.

Handling Other Messages
-----------------------

If the `gen_server` is to be able to receive other messages than requests,
the callback function `handle_info(Info, State)` must be implemented
to handle them. Examples of other messages are exit messages,
if the `gen_server` is linked to other processes than the supervisor
and it is trapping exit signals.

```erlang
handle_info({'EXIT', Pid, Reason}, State) ->
Expand Down
Loading

0 comments on commit 0ff17dc

Please sign in to comment.