• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 14793244879

02 May 2025 10:11AM UTC coverage: 44.318% (+0.1%) from 44.223%
14793244879

Pull #2542

github

web-flow
Merge fe276f187 into 2cf24f244
Pull Request #2542: Fixing thread sanitizer issues

254 of 364 new or added lines in 18 files covered. (69.78%)

77 existing lines in 8 files now uncovered.

14756 of 33296 relevant lines covered (44.32%)

291.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.81
/src/mavsdk/core/udp_connection.cpp
1
#include "udp_connection.h"
2
#include "log.h"
3

4
#ifdef WINDOWS
5
#include <winsock2.h>
6
#include <Ws2tcpip.h> // For InetPton
7
#undef SOCKET_ERROR // conflicts with ConnectionResult::SocketError
8
#ifndef MINGW
9
#pragma comment(lib, "Ws2_32.lib") // Without this, Ws2_32.lib is not included in static library.
10
#endif
11
#else
12
#include <netinet/in.h>
13
#include <sys/socket.h>
14
#include <sys/time.h>
15
#include <arpa/inet.h>
16
#include <errno.h>
17
#endif
18

19
#include <algorithm>
20
#include <utility>
21
#include <sstream>
22

23
#ifdef WINDOWS
24
#define GET_ERROR(_x) WSAGetLastError()
25
#else
26
#define GET_ERROR(_x) strerror(_x)
27
#endif
28

29
namespace mavsdk {
30

31
UdpConnection::UdpConnection(
84✔
32
    Connection::ReceiverCallback receiver_callback,
33
    std::string local_ip,
34
    int local_port_number,
35
    ForwardingOption forwarding_option) :
84✔
36
    Connection(std::move(receiver_callback), forwarding_option),
84✔
37
    _local_ip(std::move(local_ip)),
84✔
38
    _local_port_number(local_port_number)
252✔
39
{}
84✔
40

41
UdpConnection::~UdpConnection()
168✔
42
{
43
    // If no one explicitly called stop before, we should at least do it.
44
    stop();
84✔
45
}
168✔
46

47
ConnectionResult UdpConnection::start()
84✔
48
{
49
    if (!start_mavlink_receiver()) {
84✔
50
        return ConnectionResult::ConnectionsExhausted;
×
51
    }
52

53
    ConnectionResult ret = setup_port();
84✔
54
    if (ret != ConnectionResult::Success) {
84✔
55
        return ret;
×
56
    }
57

58
    start_recv_thread();
84✔
59

60
    return ConnectionResult::Success;
84✔
61
}
62

63
ConnectionResult UdpConnection::setup_port()
84✔
64
{
65
#ifdef WINDOWS
66
    WSADATA wsa;
67
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
68
        LogErr() << "Error: Winsock failed, error: %d", WSAGetLastError();
69
        return ConnectionResult::SocketError;
70
    }
71
#endif
72

73
    _socket_fd.reset(socket(AF_INET, SOCK_DGRAM, 0));
84✔
74

75
    if (_socket_fd.empty()) {
84✔
76
        LogErr() << "socket error" << GET_ERROR(errno);
×
77
        return ConnectionResult::SocketError;
×
78
    }
79

80
    struct sockaddr_in addr {};
84✔
81
    addr.sin_family = AF_INET;
84✔
82
    if (inet_pton(AF_INET, _local_ip.c_str(), &(addr.sin_addr)) != 1) {
84✔
83
        LogErr() << "inet_pton failure for address: " << _local_ip;
×
84
        return ConnectionResult::SocketError;
×
85
    }
86
    addr.sin_port = htons(_local_port_number);
84✔
87

88
    if (bind(_socket_fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != 0) {
84✔
89
        LogErr() << "bind error: " << GET_ERROR(errno);
×
90
        return ConnectionResult::BindError;
×
91
    }
92

93
    // Set receive timeout cross-platform
94
    const unsigned timeout_ms = 500;
84✔
95

96
#if defined(WINDOWS)
97
    setsockopt(
98
        _socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
99
#else
100
    struct timeval tv;
84✔
101
    tv.tv_sec = 0;
84✔
102
    tv.tv_usec = timeout_ms * 1000;
84✔
103
    setsockopt(_socket_fd.get(), SOL_SOCKET, SO_RCVTIMEO, (const void*)&tv, sizeof(tv));
84✔
104
#endif
105

106
    return ConnectionResult::Success;
84✔
107
}
108

109
void UdpConnection::start_recv_thread()
84✔
110
{
111
    _recv_thread = std::make_unique<std::thread>(&UdpConnection::receive, this);
84✔
112
}
84✔
113

114
ConnectionResult UdpConnection::stop()
84✔
115
{
116
    _should_exit = true;
84✔
117

118
    if (_recv_thread) {
84✔
119
        _recv_thread->join();
84✔
120
        _recv_thread.reset();
84✔
121
    }
122

123
    _socket_fd.close();
84✔
124

125
    // We need to stop this after stopping the receive thread, otherwise
126
    // it can happen that we interfere with the parsing of a message.
127
    stop_mavlink_receiver();
84✔
128

129
    return ConnectionResult::Success;
84✔
130
}
131

132
std::pair<bool, std::string> UdpConnection::send_message(const mavlink_message_t& message)
1,926✔
133
{
134
    std::pair<bool, std::string> result;
1,926✔
135

136
    std::lock_guard<std::mutex> lock(_remote_mutex);
1,926✔
137

138
    // Remove inactive remotes before sending messages
139
    auto now = std::chrono::steady_clock::now();
1,926✔
140

141
    _remotes.erase(
7,704✔
142
        std::remove_if(
3,852✔
143
            _remotes.begin(),
144
            _remotes.end(),
145
            [&now, this](const Remote& remote) {
3,852✔
146
                auto elapsed = now - remote.last_activity;
1,926✔
147
                bool inactive = elapsed > REMOTE_TIMEOUT;
1,926✔
148

149
                if (inactive) {
1,925✔
150
                    LogInfo() << "Removing inactive remote: " << remote.ip << ":"
×
151
                              << remote.port_number;
×
152
                }
153

154
                return inactive;
1,925✔
155
            }),
156
        _remotes.end());
3,852✔
157

158
    if (_remotes.size() == 0) {
1,926✔
159
        result.first = false;
×
160
        result.second = "no remotes";
×
161
        return result;
×
162
    }
163

164
    // Send the message to all the remotes. A remote is a UDP endpoint
165
    // identified by its <ip, port>. This means that if we have two
166
    // systems on two different endpoints, then messages directed towards
167
    // only one system will be sent to both remotes. The systems are
168
    // then expected to ignore messages that are not directed to them.
169

170
    // For multiple remotes, we ignore errors, for just one, we bubble it up.
171
    result.first = true;
1,925✔
172

173
    for (auto& remote : _remotes) {
3,850✔
174
        struct sockaddr_in dest_addr {};
1,925✔
175
        dest_addr.sin_family = AF_INET;
1,925✔
176

177
        if (inet_pton(AF_INET, remote.ip.c_str(), &dest_addr.sin_addr.s_addr) != 1) {
1,925✔
178
            std::stringstream ss;
×
179
            ss << "inet_pton failure for: " << remote.ip << ":" << remote.port_number;
×
180
            LogErr() << ss.str();
×
181
            result.first = false;
×
182
            if (!result.second.empty()) {
×
183
                result.second += ", ";
×
184
            }
185
            result.second += ss.str();
×
186
            continue;
×
187
        }
×
188
        dest_addr.sin_port = htons(remote.port_number);
1,926✔
189

190
        uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
1,926✔
191
        uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
1,926✔
192

193
        const auto send_len = sendto(
1,926✔
194
            _socket_fd.get(),
195
            reinterpret_cast<char*>(buffer),
196
            buffer_len,
197
            0,
198
            reinterpret_cast<const sockaddr*>(&dest_addr),
199
            sizeof(dest_addr));
200

201
        if (send_len != buffer_len) {
1,926✔
202
            std::stringstream ss;
×
203
            ss << "sendto failure: " << GET_ERROR(errno) << " for: " << remote.ip << ":"
×
204
               << remote.port_number;
×
205
            LogErr() << ss.str();
×
206
            result.first = false;
×
207
            if (!result.second.empty()) {
×
208
                result.second += ", ";
×
209
            }
210
            result.second += ss.str();
×
211
            continue;
×
212
        }
×
213
    }
214

215
    return result;
216
}
1,926✔
217

218
void UdpConnection::add_remote(const std::string& remote_ip, const int remote_port)
42✔
219
{
220
    add_remote_with_remote_sysid(remote_ip, remote_port, 0);
42✔
221
}
42✔
222

223
void UdpConnection::add_remote_with_remote_sysid(
1,968✔
224
    const std::string& remote_ip, const int remote_port, const uint8_t remote_sysid)
225
{
226
    std::lock_guard<std::mutex> lock(_remote_mutex);
1,968✔
227
    Remote new_remote;
1,968✔
228
    new_remote.ip = remote_ip;
1,968✔
229
    new_remote.port_number = remote_port;
1,968✔
230
    new_remote.last_activity = std::chrono::steady_clock::now();
1,968✔
231

232
    auto existing_remote =
1,967✔
233
        std::find_if(_remotes.begin(), _remotes.end(), [&new_remote](Remote& remote) {
1,967✔
234
            return remote == new_remote;
1,884✔
235
        });
236

237
    if (existing_remote == _remotes.end()) {
1,968✔
238
        // System with sysid 0 is a bit special: it is a placeholder for a connection initiated
239
        // by MAVSDK. As such, it should not be advertised as a newly discovered system.
240
        if (static_cast<int>(remote_sysid) != 0) {
84✔
241
            LogInfo() << "New system on: " << new_remote.ip << ":" << new_remote.port_number
84✔
242
                      << " (with system ID: " << static_cast<int>(remote_sysid) << ")";
84✔
243
        }
244
        _remotes.push_back(new_remote);
84✔
245
    } else {
246
        // Update the timestamp for the existing remote
247
        existing_remote->last_activity = std::chrono::steady_clock::now();
1,883✔
248
    }
249
}
1,967✔
250

251
void UdpConnection::receive()
84✔
252
{
253
    // Enough for MTU 1500 bytes.
254
    char buffer[2048];
84✔
255

256
    while (!_should_exit) {
2,291✔
257
        struct sockaddr_in src_addr = {};
2,233✔
258
        socklen_t src_addr_len = sizeof(src_addr);
2,232✔
259
        const auto recv_len = recvfrom(
2,220✔
260
            _socket_fd.get(),
261
            buffer,
262
            sizeof(buffer),
263
            0,
264
            reinterpret_cast<struct sockaddr*>(&src_addr),
265
            &src_addr_len);
266

267
        if (recv_len == 0) {
2,232✔
268
            // This can happen when shutdown is called on the socket,
269
            // therefore we check _should_exit again.
UNCOV
270
            continue;
×
271
        }
272

273
        if (recv_len < 0) {
2,232✔
274
            // This happens on destruction when _socket_fd.close() is called,
275
            // therefore be quiet.
276
            // LogErr() << "recvfrom error: " << GET_ERROR(errno);
277
            continue;
306✔
278
        }
279

280
        _mavlink_receiver->set_new_datagram(buffer, static_cast<int>(recv_len));
1,926✔
281

282
        // Parse all mavlink messages in one datagram. Once exhausted, we'll exit while.
283
        while (_mavlink_receiver->parse_message()) {
3,852✔
284
            const uint8_t sysid = _mavlink_receiver->get_last_message().sysid;
1,926✔
285

286
            if (sysid != 0) {
1,925✔
287
                char ip_str[INET_ADDRSTRLEN];
1,926✔
288
                if (inet_ntop(AF_INET, &src_addr.sin_addr, ip_str, INET_ADDRSTRLEN) != nullptr) {
1,926✔
289
                    add_remote_with_remote_sysid(ip_str, ntohs(src_addr.sin_port), sysid);
1,926✔
290
                } else {
291
                    LogErr() << "inet_ntop failure for: " << strerror(errno);
×
292
                }
293
            }
294

295
            receive_message(_mavlink_receiver->get_last_message(), this);
1,925✔
296
        }
297
    }
298
}
84✔
299

300
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc