Top | ![]() |
![]() |
![]() |
![]() |
InfBuffer * | buffer | Read / Write / Construct Only |
InfAdoptedStateVector * | buffer-modified-state | Read |
InfAdoptedStateVector * | current-state | Read |
guint | max-total-log-size | Read / Write / Construct Only |
InfUserTable * | user-table | Read / Write / Construct Only |
void | begin-execute-request | Run Last |
void | can-redo-changed | Run Last |
void | can-undo-changed | Run Last |
void | end-execute-request | Run Last |
enum | InfAdoptedAlgorithmError |
struct | InfAdoptedAlgorithm |
struct | InfAdoptedAlgorithmClass |
InfAdoptedAlgorithm implements the adOPTed algorithm for concurrency control as described in the paper "An integrating, transformation-oriented approach to concurrency control and undo in group editors" by Matthias Ressel, Doris Nitsche-Ruhland and Rul Gunzenhäuser (http://portal.acm.org/citation.cfm?id=240305).
It is based on requests, represented by the InfAdoptedRequest class. If
there is at least one local InfUser in the algorithm's user table, then
you can create own requests with the
inf_adopted_algorithm_generate_request()
function.
Remote requests can be applied via
inf_adopted_algorithm_execute_request()
. This class does not take care of
transfering the generated requests to other users which is the scope of
InfAdoptedSession.
The implementation is not tied to text editing. It can handle any operations implementing InfAdoptedOperation as long as they define sufficient transformation functions. The libinftext library provides operations for text editing, see InfTextInsertOperation and InfTextDeleteOperation.
InfAdoptedAlgorithm * inf_adopted_algorithm_new (InfUserTable *user_table
,InfBuffer *buffer
);
Creates a InfAdoptedAlgorithm.
[constructor]
InfAdoptedAlgorithm * inf_adopted_algorithm_new_full (InfUserTable *user_table
,InfBuffer *buffer
,guint max_total_log_size
);
Note that it is possible that request logs need to grow a bit larger than
max_total_log_size
in high-latency situations or when a user does not send
status updates frequently. However, when all requests have been
processed by all users, the sum of all requests in the logs is guaranteed
to be lower or equal to this value.
Set to G_MAXUINT
to disable limitation. In theory, this would allow
everyone to undo every operation up to the first one ever made. In practise,
this issues a huge amount of data that needs to be synchronized on user
join and is too expensive to compute anyway.
The default value is 2048.
[constructor]
InfAdoptedStateVector *
inf_adopted_algorithm_get_current (InfAdoptedAlgorithm *algorithm
);
Returns the current vector time of algorithm
.
InfAdoptedRequest *
inf_adopted_algorithm_get_execute_request
(InfAdoptedAlgorithm *algorithm
);
Returns whether the algorithm is currently transforming a request to the
current state and appling its state to the buffer. If it is the function
is returning the request that was received and is currently being
executed, other wise the function returns NULL
. Note that the request
execution is not re-entrant, i.e. two requests cannot be executed
concurrently at the same time, or recursively.
InfAdoptedRequest * inf_adopted_algorithm_generate_request (InfAdoptedAlgorithm *algorithm
,InfAdoptedRequestType type
,InfAdoptedUser *user
,InfAdoptedOperation *operation
);
Creates a new request that can be applied to the current document state.
The request is made by the given user. If operation is of type
INF_ADOPTED_REQUEST_DO
, then operation
specifies the operation to be
performed. Otherwise, operation
must be NULL
.
To apply the effect of the request to the document, run
inf_adopted_algorithm_execute_request()
. Note that even if the effect
is already applied to the document, the function must still be called
with the apply
parameter set to FALSE
, so that the algorithm knows that
the request has been applied.
InfAdoptedRequest * inf_adopted_algorithm_translate_request (InfAdoptedAlgorithm *algorithm
,InfAdoptedRequest *request
,InfAdoptedStateVector *to
);
Translates request
so that it can be applied to the document at state to
.
request
will not be modified but a new, translated request is returned
instead.
There are several preconditions for this function to be called. to
must
be a reachable point in the state space. Also, requests can only be
translated in forward direction, so request
's vector time must be
causally before (see inf_adopted_state_vector_causally_before()
) to
.
A new or cached InfAdoptedRequest. Free with
g_object_unref()
when no longer needed.
[transfer full]
gboolean inf_adopted_algorithm_execute_request (InfAdoptedAlgorithm *algorithm
,InfAdoptedRequest *request
,gboolean apply
,GError **error
);
This function transforms the given request such that it can be applied to the current document state and then applies it the buffer and adds it to the request log of the algorithm, so that it is used for future transformations of other requests.
If apply
is FALSE
then the request is not applied to the buffer. In this
case, it is assumed that the buffer is already modified, and that the
request is made as a result from the buffer modification. This also means
that the request must be applicable to the current document state, without
requiring transformation.
In addition, the function emits the
“begin-execute-request” and
“end-execute-request” signals, and makes
inf_adopted_algorithm_get_execute_request()
return request
during that
period.
This allows other code to hook in before and after request processing. This does not cause any loss of generality because this function is not re-entrant anyway: it cannot work when used concurrently by multiple threads nor in a recursive manner, because only when one request has been added to the log the next request can be translated, since it might need the previous request for the translation path and it needs to be translated to a state where the effect of the previous request is included so that it can consistently applied to the buffer.
There are also runtime errors that can occur if request
execution fails.
In this case the function returns FALSE
and error
is set. Possible
reasons for this include request
being an INF_ADOPTED_REQUEST_UNDO
or
INF_ADOPTED_REQUEST_REDO
request without there being an operation to
undo or redo, or if the translated operation cannot be applied to the
buffer. This usually means that the input request
was invalid. However,
this is not considered a programmer error because typically requests are
received from untrusted input sources such as network connections.
Note that there cannot be any runtime errors if apply
is set to FALSE
.
In that case it is safe to call the function with NULL
error.
void
inf_adopted_algorithm_cleanup (InfAdoptedAlgorithm *algorithm
);
Removes requests in all users request logs which are no longer needed. This includes requests which cannot be undone or redone anymore due to the constraints of the “max-total-log-size” property, and requests that every participant is guaranteed to have processed already.
This function can be called after every executed request to keep memory use to a minimum, or it can be called in regular intervals, or it can also be omitted if the request history should be preserved.
gboolean inf_adopted_algorithm_can_undo (InfAdoptedAlgorithm *algorithm
,InfAdoptedUser *user
);
Returns whether user
can issue an undo request in the current state. Note
that if user
is non-local, then the result of this function does not
depend on the current state but on the state that we know user
is
guaranteed to have reached. This is because user
might still issue an
Undo request even if the max-total-log-size is already exceeded if user
does not know yet that it is exceeded.
gboolean inf_adopted_algorithm_can_redo (InfAdoptedAlgorithm *algorithm
,InfAdoptedUser *user
);
Returns whether user
can issue a redo request in the current state. Note
that if user
is non-local, then the result of this function does not
depend on the current state but on the state that we know user
is
guaranteed to have reached. This is because user
might still issue a
Redo request even if the max-total-log-size is already exceeded if user
does not know yet that it is exceeded.
Error codes for InfAdoptedAlgorithm. These can occur when invalid requests
are passed to inf_adopted_algorithm_execute_request()
.
struct InfAdoptedAlgorithm;
InfAdoptedAlgorithm is an opaque data type. You should only access it via the public API functions.
struct InfAdoptedAlgorithmClass { void(*can_undo_changed)(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, gboolean can_undo); void(*can_redo_changed)(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, gboolean can_redo); void(*begin_execute_request)(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, InfAdoptedRequest* request); void(*end_execute_request)(InfAdoptedAlgorithm* algorithm, InfAdoptedUser* user, InfAdoptedRequest* request, InfAdoptedRequest* translated_request, const GError* error); };
Signals for the InfAdoptedAlgorithm class.
Default signal handler for the “can_undo_changed” signal. |
||
Default signal handler for the “can_redo_changed” signal. |
||
Default signal handler for the “begin-execute-request” signal. |
||
Default signal handler for the “end-execute-request” signal. |
“buffer”
property“buffer” InfBuffer *
The buffer to apply operations to.
Owner: InfAdoptedAlgorithm
Flags: Read / Write / Construct Only
“buffer-modified-state”
property“buffer-modified-state” InfAdoptedStateVector *
The state in which the buffer is considered not being modified.
Owner: InfAdoptedAlgorithm
Flags: Read
“current-state”
property“current-state” InfAdoptedStateVector *
The state vector describing the current document state.
Owner: InfAdoptedAlgorithm
Flags: Read
“max-total-log-size”
property “max-total-log-size” guint
The maximum number of requests to keep in all user's logs.
Owner: InfAdoptedAlgorithm
Flags: Read / Write / Construct Only
Default value: 2048
“user-table”
property“user-table” InfUserTable *
The user table.
Owner: InfAdoptedAlgorithm
Flags: Read / Write / Construct Only
“begin-execute-request”
signalvoid user_function (InfAdoptedAlgorithm *algorithm, InfAdoptedUser *user, InfAdoptedRequest *request, gpointer user_data)
This signal is emitted every time the algorithm executes a request, i.e. transforms it such that it can be applied to the current state, resolves undo/redo operations and applies the resulting operation to the buffer.
algorithm |
The InfAdoptedAlgorithm executing a request. |
|
user |
The InfAdoptedUser executing the request. |
|
request |
The InfAdoptedRequest being executed. |
|
user_data |
user data set when the signal handler was connected. |
Flags: Run Last
“can-redo-changed”
signalvoid user_function (InfAdoptedAlgorithm *algorithm, InfAdoptedUser *user, gboolean can_undo, gpointer user_data)
This signal is emitted every time the can-redo state of a local user
in algorithm
's user table changed. The can-redo state defines whether
user
can generate a redo request
(via inf_adopted_algorithm_generate_request()
) in the current situation, see
also inf_adopted_algorithm_can_redo()
.
algorithm |
The InfAdoptedAlgorithm for which a user's can-redo state changed. |
|
user |
The InfAdoptedUser whose can-redo state has changed. |
|
can_undo |
Whether |
|
user_data |
user data set when the signal handler was connected. |
Flags: Run Last
“can-undo-changed”
signalvoid user_function (InfAdoptedAlgorithm *algorithm, InfAdoptedUser *user, gboolean can_undo, gpointer user_data)
This signal is emitted every time the can-undo state of a local user
in algorithm
's user table changed. The can-undo state defines whether
user
can generate an undo request
(via inf_adopted_algorithm_generate_request()
) in the current situation, see
also inf_adopted_algorithm_can_undo()
.
algorithm |
The InfAdoptedAlgorithm for which a user's can-undo state changed. |
|
user |
The InfAdoptedUser whose can-undo state has changed. |
|
can_undo |
Whether |
|
user_data |
user data set when the signal handler was connected. |
Flags: Run Last
“end-execute-request”
signalvoid user_function (InfAdoptedAlgorithm *algorithm, InfAdoptedUser *user, InfAdoptedRequest *request, InfAdoptedRequest *translated, GError *error, gpointer user_data)
This signal is emitted after a request has been executed. The request
parameter is not necessarily the same as the one in the corresponding
emission of “begin-execute-request”, however its
effect on the buffer is the same. The difference is that the request in
this signal might be reversible while the request in the
“begin-execute-request” emission might not be
reversible. The algorithm can make some requests reversible during
their execution.
The translated
request is the result of the transformation, i.e. it is
always a INF_ADOPTED_REQUEST_DO
type request and its state vector
corresponds to the current state. It has already been applied on the
buffer by the algorithm. If request
is of type INF_ADOPTED_REQUEST_UNDO
or INF_ADOPTED_REQUEST_REDO
then translated
represents the operation
that actually was performed on the buffer to undo or redo the effect of
a previous request.
It can happen that an error occurs during execution. Usually this is due
to invalid input, such as a request that cannot be transformed to the
current state, a INF_ADOPTED_REQUEST_UNDO
request which has no
corresponding INF_ADOPTED_REQUEST_DO
or INF_ADOPTED_REQUEST_REDO
request, or a request that ends up in an invalid operation (e.g.
inserting text behind the end of the document). If such an error occurs
then request
is the same as the one in the
InfAdoptedAlgorithm
::begin-execute-request emission, translated
may or
may not be NULL
and error
contains information on the error occurred.
algorithm |
The InfAdoptedAlgorithm executing a request. |
|
user |
The InfAdoptedUser executing the request. |
|
request |
The InfAdoptedRequest that was executed. |
|
translated |
The result of the request transformation, or |
|
error |
The error that occurred during execution, or |
|
user_data |
user data set when the signal handler was connected. |
Flags: Run Last