static void cmp(const char* should_be, InfAdoptedStateVector* vec) { char* is; InfAdoptedStateVector* should_be_vec; is = inf_adopted_state_vector_to_string(vec); if (strcmp(should_be, is) != 0) { printf("should be: %s\n" "is: %s\n" "strcmp failed\n", should_be, is); g_assert_not_reached(); } should_be_vec = inf_adopted_state_vector_from_string(should_be, NULL); if (!should_be_vec || inf_adopted_state_vector_compare(vec, should_be_vec) != 0 || inf_adopted_state_vector_compare(should_be_vec, vec) != 0) { printf("should be: %s\n" "is: %s\n" "compare failed\n", should_be, is); g_assert_not_reached(); } g_free(is); inf_adopted_state_vector_free(should_be_vec); printf("ok!\n"); }
/** * inf_adopted_request_need_concurrency_id: * @request: The request to transform. * @against: The request to transform against. * * Returns whether transforming @request against @against requires a * concurrency ID. You can still call inf_adopted_request_transform() with * a concurrency ID of %INF_ADOPTED_CONCURRENCY_NONE even if this function * returns %TRUE if you don't have another possibility to find a * concurrency ID in which case user IDs are used to determine which request * to transform. * * Both request need to be of type %INF_ADOPTED_REQUEST_DO, and their state * vectors must be the same. * * Returns: Whether transformation of @request against @against requires a * concurrency ID. */ gboolean inf_adopted_request_need_concurrency_id(InfAdoptedRequest* request, InfAdoptedRequest* against) { InfAdoptedRequestPrivate* request_priv; InfAdoptedRequestPrivate* against_priv; g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), FALSE); g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(against), FALSE); request_priv = INF_ADOPTED_REQUEST_PRIVATE(request); against_priv = INF_ADOPTED_REQUEST_PRIVATE(against); g_return_val_if_fail(request_priv->type == INF_ADOPTED_REQUEST_DO, FALSE); g_return_val_if_fail(against_priv->type == INF_ADOPTED_REQUEST_DO, FALSE); g_return_val_if_fail(request_priv->user_id != against_priv->user_id, FALSE); g_return_val_if_fail( inf_adopted_state_vector_compare( request_priv->vector, against_priv->vector ) == 0, FALSE ); return inf_adopted_operation_need_concurrency_id( request_priv->operation, against_priv->operation ); }
/** * inf_adopted_request_get_concurrency_id: * @request: The request to transform. * @against: The request to transform against. * * Returns a concurrency ID for transformation of @operation against @against. * It always returns %INF_ADOPTED_CONCURRENCY_NONE when * inf_adopted_request_need_concurrency_id() returns %TRUE (but that's not * necessarily true the other way around), since it is not possible to decide * which operation to transform without any additional information. * * However, the function can be called on the same requests in a previous * state. In some cases, a decision can be made based on these previous * requests. This can be used as a concurrency ID for a call to * inf_adopted_request_transform(). If this does not yield a decision, it is * still possible to call inf_adopted_request_transform() with * %INF_ADOPTED_CONCURRENCY_NONE as concurrency ID in which case an arbitrary * request will be transformed, based on the user IDs of the requests. * * Both requests must be of type %INF_ADOPTED_REQUEST_DO, and their state * vectors must be the same. * * Returns: A concurrency ID between @operation and @against. Can be * %INF_ADOPTED_CONCURRENCY_NONE in case no decision can be made. */ InfAdoptedConcurrencyId inf_adopted_request_get_concurrency_id(InfAdoptedRequest* request, InfAdoptedRequest* against) { InfAdoptedRequestPrivate* request_priv; InfAdoptedRequestPrivate* against_priv; g_return_val_if_fail( INF_ADOPTED_IS_REQUEST(request), INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( INF_ADOPTED_IS_REQUEST(against), INF_ADOPTED_CONCURRENCY_NONE ); request_priv = INF_ADOPTED_REQUEST_PRIVATE(request); against_priv = INF_ADOPTED_REQUEST_PRIVATE(against); g_return_val_if_fail( request_priv->type == INF_ADOPTED_REQUEST_DO, INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( against_priv->type == INF_ADOPTED_REQUEST_DO, INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( request_priv->user_id != against_priv->user_id, INF_ADOPTED_CONCURRENCY_NONE ); g_return_val_if_fail( inf_adopted_state_vector_compare( request_priv->vector, against_priv->vector ) == 0, INF_ADOPTED_CONCURRENCY_NONE ); return inf_adopted_operation_get_concurrency_id( request_priv->operation, against_priv->operation ); }
static void inf_adopted_session_local_user_added(InfAdoptedSession* session, InfAdoptedUser* user) { InfAdoptedSessionPrivate* priv; InfSessionStatus status; InfAdoptedSessionLocalUser* local; InfAdoptedStateVector* current_state; priv = INF_ADOPTED_SESSION_PRIVATE(session); status = inf_session_get_status(INF_SESSION(session)); /* Cannot be local while synchronizing */ g_assert(status == INF_SESSION_RUNNING); local = g_slice_new(InfAdoptedSessionLocalUser); local->user = user; local->last_send_vector = inf_adopted_state_vector_copy( inf_adopted_user_get_vector(user) ); /* Set current vector for local user, this is kept up-to-date by * InfAdoptedAlgorithm. TODO: Also do this in InfAdoptedAlgorithm? */ inf_adopted_user_set_vector( user, inf_adopted_state_vector_copy( inf_adopted_algorithm_get_current(priv->algorithm) ) ); local->noop_time = 0; priv->local_users = g_slist_prepend(priv->local_users, local); /* Start noop timer if user is not up to date */ current_state = inf_adopted_algorithm_get_current(priv->algorithm); if(inf_adopted_state_vector_compare(current_state, local->last_send_vector)) inf_adopted_session_start_noop_timer(session, local); }
/** * inf_adopted_request_transform: * @request: The request to transform. * @against: The request to transform against. * @concurrency_id: A concurrency ID for the transformation. * * Transforms the operation of @request against the operation of @against. * Both requests must be of type %INF_ADOPTED_REQUEST_DO, and their state * vectors must be the same. * * @concurrency_id can be %INF_ADOPTED_CONCURRENCY_NONE even if the * transformation requires a concurrency ID (see * inf_adopted_request_need_concurrency_id()). In that case, it is assumed * that it does not matter which operation to transform, and user IDs are * used to determine a concurrency ID for the transformation. * * Returns: A new #InfAdoptedRequest, the result of the transformation. **/ InfAdoptedRequest* inf_adopted_request_transform(InfAdoptedRequest* request, InfAdoptedRequest* against, InfAdoptedConcurrencyId concurrency_id) { InfAdoptedRequestPrivate* request_priv; InfAdoptedRequestPrivate* against_priv; InfAdoptedOperation* new_operation; InfAdoptedStateVector* new_vector; InfAdoptedRequest* new_request; g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(request), NULL); g_return_val_if_fail(INF_ADOPTED_IS_REQUEST(against), NULL); request_priv = INF_ADOPTED_REQUEST_PRIVATE(request); against_priv = INF_ADOPTED_REQUEST_PRIVATE(against); g_return_val_if_fail(request_priv->type == INF_ADOPTED_REQUEST_DO, NULL); g_return_val_if_fail(against_priv->type == INF_ADOPTED_REQUEST_DO, NULL); g_return_val_if_fail(request_priv->user_id != against_priv->user_id, NULL); g_return_val_if_fail( inf_adopted_state_vector_compare( request_priv->vector, against_priv->vector ) == 0, NULL ); if(concurrency_id != INF_ADOPTED_CONCURRENCY_NONE) { new_operation = inf_adopted_operation_transform( request_priv->operation, against_priv->operation, concurrency_id ); } else if(request_priv->user_id > against_priv->user_id) { new_operation = inf_adopted_operation_transform( request_priv->operation, against_priv->operation, INF_ADOPTED_CONCURRENCY_OTHER ); } else { new_operation = inf_adopted_operation_transform( request_priv->operation, against_priv->operation, INF_ADOPTED_CONCURRENCY_SELF ); } new_vector = inf_adopted_state_vector_copy(request_priv->vector); inf_adopted_state_vector_add(new_vector, against_priv->user_id, 1); new_request = inf_adopted_request_new_do( new_vector, request_priv->user_id, new_operation ); g_object_unref(new_operation); inf_adopted_state_vector_free(new_vector); return new_request; }