Files
grd/grd-rdp-connect-time-autodetection.c
2026-02-13 13:06:50 +09:00

644 lines
22 KiB
C

/*
* Copyright (C) 2023 Pascal Nowack
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include "grd-rdp-connect-time-autodetection.h"
#define CT_BW_MAX_TOLERABLE_RESPONSE_LATENCY_US (400 * 1000)
#define CT_BW_MAX_TOLERABLE_TIME_DELTA_MS 100
#define CT_N_PINGS 10
#define CT_PING_INTERVAL_MS 10
#define BW_MEASURE_SEQUENCE_NUMBER 0
#define CT_NON_RTT_SEQUENCE_NUMBER 0
typedef enum
{
CT_AUTODETECT_STATE_NONE,
CT_AUTODETECT_STATE_MEASURE_BW_1,
CT_AUTODETECT_STATE_MEASURE_BW_2,
CT_AUTODETECT_STATE_MEASURE_BW_3,
CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1,
CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2,
CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3,
CT_AUTODETECT_STATE_START_RTT_DETECTION,
CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE,
CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT,
CT_AUTODETECT_STATE_COMPLETE,
} CtAutodetectState;
struct _GrdRdpConnectTimeAutodetection
{
GObject parent;
GrdRdpNetworkAutodetection *network_autodetection;
rdpAutoDetect *rdp_autodetect;
gboolean protocol_stopped;
GMutex ct_autodetection_mutex;
CtAutodetectState state;
GSource *sync_source;
GCond net_char_sync_cond;
GMutex net_char_sync_mutex;
gboolean pending_net_char_sync;
GSource *ping_source;
uint32_t pending_pings;
gboolean pending_last_sequence_number;
uint16_t last_sequence_number;
int64_t base_round_trip_time_us;
int64_t avg_round_trip_time_us;
int64_t bw_measure_time_us;
uint32_t last_time_delta_ms;
uint32_t last_byte_count;
};
G_DEFINE_TYPE (GrdRdpConnectTimeAutodetection, grd_rdp_connect_time_autodetection,
G_TYPE_OBJECT)
gboolean
grd_rdp_connect_time_autodetection_is_complete (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
g_autoptr (GMutexLocker) locker = NULL;
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
return ct_autodetection->state == CT_AUTODETECT_STATE_COMPLETE;
}
static void
determine_bw_measure_payloads (GrdRdpConnectTimeAutodetection *ct_autodetection,
uint16_t *payload_lengths,
uint32_t *n_payloads)
{
switch (ct_autodetection->state)
{
case CT_AUTODETECT_STATE_MEASURE_BW_1:
*payload_lengths = 15 * 1024 + 512 + 256 + 128 + 64;
*n_payloads = 1;
break;
case CT_AUTODETECT_STATE_MEASURE_BW_2:
*payload_lengths = 15 * 1024 + 512 + 256 + 128 + 64;
*n_payloads = 4;
break;
case CT_AUTODETECT_STATE_MEASURE_BW_3:
*payload_lengths = 15 * 1024 + 512 + 256 + 128 + 64;
*n_payloads = 16;
break;
default:
g_assert_not_reached ();
}
}
static CtAutodetectState
get_next_bw_measure_state (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
switch (ct_autodetection->state)
{
case CT_AUTODETECT_STATE_MEASURE_BW_1:
return CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1;
case CT_AUTODETECT_STATE_MEASURE_BW_2:
return CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2;
case CT_AUTODETECT_STATE_MEASURE_BW_3:
return CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
return CT_AUTODETECT_STATE_MEASURE_BW_2;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
return CT_AUTODETECT_STATE_MEASURE_BW_3;
default:
g_assert_not_reached ();
}
return CT_AUTODETECT_STATE_NONE;
}
static void
measure_connect_time_bandwidth (GrdRdpConnectTimeAutodetection *ct_autodetection,
uint16_t payload_lengths,
uint32_t n_payloads)
{
rdpAutoDetect *rdp_autodetect = ct_autodetection->rdp_autodetect;
uint32_t i;
g_assert (payload_lengths > 0);
g_assert (n_payloads >= 1);
rdp_autodetect->BandwidthMeasureStart (rdp_autodetect, RDP_TRANSPORT_TCP,
BW_MEASURE_SEQUENCE_NUMBER);
for (i = 0; i < n_payloads - 1; ++i)
{
rdp_autodetect->BandwidthMeasurePayload (rdp_autodetect,
RDP_TRANSPORT_TCP,
BW_MEASURE_SEQUENCE_NUMBER,
payload_lengths);
}
rdp_autodetect->BandwidthMeasureStop (rdp_autodetect, RDP_TRANSPORT_TCP,
BW_MEASURE_SEQUENCE_NUMBER,
payload_lengths);
}
static const char *
ct_autodetect_state_to_string (CtAutodetectState state)
{
switch (state)
{
case CT_AUTODETECT_STATE_NONE:
return "NONE";
case CT_AUTODETECT_STATE_MEASURE_BW_1:
return "MEASURE_BW_1";
case CT_AUTODETECT_STATE_MEASURE_BW_2:
return "MEASURE_BW_2";
case CT_AUTODETECT_STATE_MEASURE_BW_3:
return "MEASURE_BW_3";
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
return "AWAIT_BW_RESULT_1";
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
return "AWAIT_BW_RESULT_2";
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
return "AWAIT_BW_RESULT_3";
case CT_AUTODETECT_STATE_START_RTT_DETECTION:
return "START_RTT_DETECTION";
case CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE:
return "AWAIT_LAST_RTT_RESPONSE";
case CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT:
return "SEND_NET_CHAR_RESULT";
case CT_AUTODETECT_STATE_COMPLETE:
return "COMPLETE";
}
g_assert_not_reached ();
}
static void
transition_to_state (GrdRdpConnectTimeAutodetection *ct_autodetection,
CtAutodetectState state)
{
g_assert (ct_autodetection->state != state);
g_debug ("[RDP] Connect-Time Autodetect: Updating state from %s to %s",
ct_autodetect_state_to_string (ct_autodetection->state),
ct_autodetect_state_to_string (state));
ct_autodetection->state = state;
}
static void
last_sequence_number_ready (gpointer user_data,
uint16_t sequence_number)
{
GrdRdpConnectTimeAutodetection *ct_autodetection = user_data;
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
ct_autodetection->last_sequence_number = sequence_number;
ct_autodetection->pending_last_sequence_number = FALSE;
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
}
static gboolean
emit_ping (gpointer user_data)
{
GrdRdpConnectTimeAutodetection *ct_autodetection = user_data;
GrdRdpNetworkAutodetection *network_autodetection =
ct_autodetection->network_autodetection;
GrdRdpNwAutodetectSequenceNumberReadyCallback sequence_number_ready = NULL;
g_assert (!ct_autodetection->protocol_stopped);
g_assert (ct_autodetection->pending_pings > 0);
--ct_autodetection->pending_pings;
if (ct_autodetection->pending_pings == 0)
{
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
g_clear_pointer (&ct_autodetection->ping_source, g_source_unref);
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
sequence_number_ready = last_sequence_number_ready;
}
grd_rdp_network_autodetection_emit_ping (network_autodetection,
sequence_number_ready,
ct_autodetection);
if (ct_autodetection->pending_pings == 0)
return G_SOURCE_REMOVE;
return G_SOURCE_CONTINUE;
}
static void
start_ping_emission (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
emit_ping (ct_autodetection);
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
ct_autodetection->ping_source = g_timeout_source_new (CT_PING_INTERVAL_MS);
g_source_set_callback (ct_autodetection->ping_source, emit_ping,
ct_autodetection, NULL);
g_source_attach (ct_autodetection->ping_source, NULL);
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
}
static void
notify_network_characteristics_result (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
rdpAutoDetect *rdp_autodetect = ct_autodetection->rdp_autodetect;
rdpNetworkCharacteristicsResult result = {};
uint64_t bit_count;
uint32_t bandwidth_kbits;
bit_count = ((uint64_t) ct_autodetection->last_byte_count) * UINT64_C (8);
bandwidth_kbits = bit_count / MAX (ct_autodetection->last_time_delta_ms, 1);
result.type = RDP_NETCHAR_RESULT_TYPE_BASE_RTT_BW_AVG_RTT;
result.baseRTT = ct_autodetection->base_round_trip_time_us / 1000;
result.averageRTT = ct_autodetection->avg_round_trip_time_us / 1000;
result.bandwidth = bandwidth_kbits;
rdp_autodetect->NetworkCharacteristicsResult (rdp_autodetect,
RDP_TRANSPORT_TCP,
CT_NON_RTT_SEQUENCE_NUMBER,
&result);
}
static FREERDP_AUTODETECT_STATE
detect_network_characteristics (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
CtAutodetectState new_state = CT_AUTODETECT_STATE_NONE;
g_autoptr (GMutexLocker) locker = NULL;
gboolean pending_bw_request = FALSE;
gboolean pending_ping_emission = FALSE;
gboolean pending_net_char_result = FALSE;
uint16_t payload_lengths = 0;
uint32_t n_payloads = 1;
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
switch (ct_autodetection->state)
{
case CT_AUTODETECT_STATE_NONE:
g_assert_not_reached ();
case CT_AUTODETECT_STATE_MEASURE_BW_1:
case CT_AUTODETECT_STATE_MEASURE_BW_2:
case CT_AUTODETECT_STATE_MEASURE_BW_3:
determine_bw_measure_payloads (ct_autodetection,
&payload_lengths, &n_payloads);
ct_autodetection->bw_measure_time_us = g_get_monotonic_time ();
new_state = get_next_bw_measure_state (ct_autodetection);
pending_bw_request = TRUE;
break;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
return FREERDP_AUTODETECT_STATE_REQUEST;
case CT_AUTODETECT_STATE_START_RTT_DETECTION:
g_assert (!ct_autodetection->ping_source);
g_assert (ct_autodetection->pending_pings >= 2);
new_state = CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE;
pending_ping_emission = TRUE;
break;
case CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE:
return FREERDP_AUTODETECT_STATE_REQUEST;
case CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT:
new_state = CT_AUTODETECT_STATE_COMPLETE;
pending_net_char_result = TRUE;
break;
case CT_AUTODETECT_STATE_COMPLETE:
return FREERDP_AUTODETECT_STATE_COMPLETE;
}
g_assert (new_state != CT_AUTODETECT_STATE_NONE);
transition_to_state (ct_autodetection, new_state);
g_clear_pointer (&locker, g_mutex_locker_free);
if (pending_bw_request)
{
measure_connect_time_bandwidth (ct_autodetection, payload_lengths,
n_payloads);
}
if (pending_ping_emission)
start_ping_emission (ct_autodetection);
if (pending_net_char_result)
{
notify_network_characteristics_result (ct_autodetection);
return FREERDP_AUTODETECT_STATE_COMPLETE;
}
return FREERDP_AUTODETECT_STATE_REQUEST;
}
void
grd_rdp_connect_time_autodetection_start_detection (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
g_assert (ct_autodetection->state == CT_AUTODETECT_STATE_NONE);
transition_to_state (ct_autodetection, CT_AUTODETECT_STATE_MEASURE_BW_1);
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
detect_network_characteristics (ct_autodetection);
}
void
grd_rdp_connect_time_autodetection_invoke_shutdown (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
g_mutex_lock (&ct_autodetection->net_char_sync_mutex);
ct_autodetection->protocol_stopped = TRUE;
g_cond_signal (&ct_autodetection->net_char_sync_cond);
g_mutex_unlock (&ct_autodetection->net_char_sync_mutex);
}
static uint32_t
get_bw_measure_phase (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
switch (ct_autodetection->state)
{
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
return 1;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
return 2;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
return 3;
default:
g_assert_not_reached ();
}
return 0;
}
BOOL
grd_rdp_connect_time_autodetection_notify_bw_measure_results (GrdRdpConnectTimeAutodetection *ct_autodetection,
uint32_t time_delta_ms,
uint32_t byte_count)
{
CtAutodetectState new_state = CT_AUTODETECT_STATE_NONE;
g_autoptr (GMutexLocker) locker = NULL;
int64_t current_time_us;
int64_t response_latency_us;
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
switch (ct_autodetection->state)
{
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
current_time_us = g_get_monotonic_time ();
response_latency_us = current_time_us - ct_autodetection->bw_measure_time_us;
if (response_latency_us < CT_BW_MAX_TOLERABLE_RESPONSE_LATENCY_US &&
time_delta_ms < CT_BW_MAX_TOLERABLE_TIME_DELTA_MS)
new_state = get_next_bw_measure_state (ct_autodetection);
else
new_state = CT_AUTODETECT_STATE_START_RTT_DETECTION;
break;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
new_state = CT_AUTODETECT_STATE_START_RTT_DETECTION;
break;
default:
g_warning ("[RDP] Connect-Time Autodetect: Received stray Bandwidth "
"Measure Results PDU (current state: %s)",
ct_autodetect_state_to_string (ct_autodetection->state));
return FALSE;
}
g_debug ("[RDP] Connect-Time Autodetect: BW Measure Phase %u: "
"time delta: %ums, byte count: %u => %uKB/s",
get_bw_measure_phase (ct_autodetection), time_delta_ms, byte_count,
byte_count / MAX (time_delta_ms, 1));
ct_autodetection->last_time_delta_ms = time_delta_ms;
ct_autodetection->last_byte_count = byte_count;
g_assert (new_state != CT_AUTODETECT_STATE_NONE);
transition_to_state (ct_autodetection, new_state);
return TRUE;
}
void
grd_rdp_connect_time_autodetection_notify_rtt_measure_response (GrdRdpConnectTimeAutodetection *ct_autodetection,
uint16_t sequence_number,
int64_t base_round_trip_time_us,
int64_t avg_round_trip_time_us)
{
g_autoptr (GMutexLocker) locker = NULL;
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
if (ct_autodetection->pending_last_sequence_number ||
ct_autodetection->last_sequence_number != sequence_number)
return;
g_assert (ct_autodetection->state == CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE);
g_debug ("[RDP] Connect-Time Autodetect: base RTT: %lims, average RTT: %lims",
base_round_trip_time_us / 1000, avg_round_trip_time_us / 1000);
ct_autodetection->base_round_trip_time_us = base_round_trip_time_us;
ct_autodetection->avg_round_trip_time_us = avg_round_trip_time_us;
transition_to_state (ct_autodetection, CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT);
}
static gboolean
sync_with_main_context (gpointer user_data)
{
GrdRdpConnectTimeAutodetection *ct_autodetection = user_data;
g_mutex_lock (&ct_autodetection->ct_autodetection_mutex);
if (ct_autodetection->ping_source)
{
g_source_destroy (ct_autodetection->ping_source);
g_clear_pointer (&ct_autodetection->ping_source, g_source_unref);
}
g_mutex_unlock (&ct_autodetection->ct_autodetection_mutex);
g_mutex_lock (&ct_autodetection->net_char_sync_mutex);
g_clear_pointer (&ct_autodetection->sync_source, g_source_unref);
ct_autodetection->pending_net_char_sync = FALSE;
g_cond_signal (&ct_autodetection->net_char_sync_cond);
g_mutex_unlock (&ct_autodetection->net_char_sync_mutex);
return G_SOURCE_REMOVE;
}
static void
sync_ct_autodetect_handling (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
g_mutex_lock (&ct_autodetection->net_char_sync_mutex);
ct_autodetection->pending_net_char_sync = TRUE;
ct_autodetection->sync_source = g_idle_source_new ();
g_source_set_callback (ct_autodetection->sync_source, sync_with_main_context,
ct_autodetection, NULL);
g_source_attach (ct_autodetection->sync_source, NULL);
while (!ct_autodetection->protocol_stopped &&
ct_autodetection->pending_net_char_sync)
{
g_cond_wait (&ct_autodetection->net_char_sync_cond,
&ct_autodetection->net_char_sync_mutex);
}
g_mutex_unlock (&ct_autodetection->net_char_sync_mutex);
}
static BOOL
autodetect_network_characteristics_sync (rdpAutoDetect *rdp_autodetect,
RDP_TRANSPORT_TYPE transport_type,
uint16_t sequenceNumber,
uint32_t bandwidth,
uint32_t rtt)
{
GrdRdpNetworkAutodetection *network_autodetection = rdp_autodetect->custom;
GrdRdpConnectTimeAutodetection *ct_autodetection =
grd_rdp_network_autodetection_get_ct_handler (network_autodetection);
g_autoptr (GMutexLocker) locker = NULL;
gboolean pending_sync = FALSE;
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
switch (ct_autodetection->state)
{
case CT_AUTODETECT_STATE_NONE:
case CT_AUTODETECT_STATE_MEASURE_BW_1:
case CT_AUTODETECT_STATE_MEASURE_BW_2:
case CT_AUTODETECT_STATE_MEASURE_BW_3:
g_assert_not_reached ();
break;
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_1:
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_2:
case CT_AUTODETECT_STATE_AWAIT_BW_RESULT_3:
g_assert (!pending_sync);
break;
case CT_AUTODETECT_STATE_START_RTT_DETECTION:
g_assert_not_reached ();
break;
case CT_AUTODETECT_STATE_AWAIT_LAST_RTT_RESPONSE:
pending_sync = TRUE;
break;
case CT_AUTODETECT_STATE_SEND_NET_CHAR_RESULT:
g_assert_not_reached ();
break;
case CT_AUTODETECT_STATE_COMPLETE:
g_warning ("[RDP] Connect-Time Autodetect: Received stray Network "
"Characteristics Sync PDU (current state: %s). Ignoring PDU...",
ct_autodetect_state_to_string (ct_autodetection->state));
return TRUE;
}
g_clear_pointer (&locker, g_mutex_locker_free);
if (pending_sync)
sync_ct_autodetect_handling (ct_autodetection);
g_debug ("[RDP] Connect-Time Autodetect: Received Sync PDU: "
"RTT: %ums, bandwidth: %uKBit/s", rtt, bandwidth);
locker = g_mutex_locker_new (&ct_autodetection->ct_autodetection_mutex);
transition_to_state (ct_autodetection, CT_AUTODETECT_STATE_COMPLETE);
return TRUE;
}
static FREERDP_AUTODETECT_STATE
autodetect_on_connect_time_autodetect_progress (rdpAutoDetect *rdp_autodetect)
{
GrdRdpNetworkAutodetection *network_autodetection = rdp_autodetect->custom;
GrdRdpConnectTimeAutodetection *ct_autodetection =
grd_rdp_network_autodetection_get_ct_handler (network_autodetection);
return detect_network_characteristics (ct_autodetection);
}
GrdRdpConnectTimeAutodetection *
grd_rdp_connect_time_autodetection_new (GrdRdpNetworkAutodetection *network_autodetection,
rdpAutoDetect *rdp_autodetect)
{
GrdRdpConnectTimeAutodetection *ct_autodetection;
ct_autodetection = g_object_new (GRD_TYPE_RDP_CONNECT_TIME_AUTODETECTION, NULL);
ct_autodetection->network_autodetection = network_autodetection;
ct_autodetection->rdp_autodetect = rdp_autodetect;
rdp_autodetect->NetworkCharacteristicsSync = autodetect_network_characteristics_sync;
rdp_autodetect->OnConnectTimeAutoDetectProgress = autodetect_on_connect_time_autodetect_progress;
return ct_autodetection;
}
static void
grd_rdp_connect_time_autodetection_dispose (GObject *object)
{
GrdRdpConnectTimeAutodetection *ct_autodetection = GRD_RDP_CONNECT_TIME_AUTODETECTION (object);
g_assert (ct_autodetection->protocol_stopped);
if (ct_autodetection->sync_source)
{
g_source_destroy (ct_autodetection->sync_source);
g_clear_pointer (&ct_autodetection->sync_source, g_source_unref);
}
if (ct_autodetection->ping_source)
{
g_source_destroy (ct_autodetection->ping_source);
g_clear_pointer (&ct_autodetection->ping_source, g_source_unref);
}
G_OBJECT_CLASS (grd_rdp_connect_time_autodetection_parent_class)->dispose (object);
}
static void
grd_rdp_connect_time_autodetection_finalize (GObject *object)
{
GrdRdpConnectTimeAutodetection *ct_autodetection = GRD_RDP_CONNECT_TIME_AUTODETECTION (object);
g_cond_clear (&ct_autodetection->net_char_sync_cond);
g_mutex_clear (&ct_autodetection->net_char_sync_mutex);
g_mutex_clear (&ct_autodetection->ct_autodetection_mutex);
G_OBJECT_CLASS (grd_rdp_connect_time_autodetection_parent_class)->finalize (object);
}
static void
grd_rdp_connect_time_autodetection_init (GrdRdpConnectTimeAutodetection *ct_autodetection)
{
ct_autodetection->state = CT_AUTODETECT_STATE_NONE;
ct_autodetection->pending_pings = CT_N_PINGS;
ct_autodetection->pending_last_sequence_number = TRUE;
g_mutex_init (&ct_autodetection->ct_autodetection_mutex);
g_mutex_init (&ct_autodetection->net_char_sync_mutex);
g_cond_init (&ct_autodetection->net_char_sync_cond);
}
static void
grd_rdp_connect_time_autodetection_class_init (GrdRdpConnectTimeAutodetectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = grd_rdp_connect_time_autodetection_dispose;
object_class->finalize = grd_rdp_connect_time_autodetection_finalize;
}