Enable build for FreeBSD (part 1/2)
[ardour.git] / libs / pbd / crossthread.win.cc
1 /*
2   Copyright (C) 2009 Paul Davis
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 CrossThreadChannel::CrossThreadChannel (bool non_blocking)
21         : receive_channel (0)
22         , receive_source (0)
23         , receive_slot ()
24         , send_socket()
25         , receive_socket()
26         , recv_address()
27 {
28         struct sockaddr_in send_address;
29
30         // Create Send Socket
31         send_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
32         send_address.sin_family = AF_INET;
33         send_address.sin_addr.s_addr = inet_addr("127.0.0.1");
34         send_address.sin_port = htons(0);
35         int status = bind(send_socket, (SOCKADDR*)&send_address,
36                           sizeof(send_address));
37
38         if (status != 0) {
39                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Send socket binding failed with error: " << WSAGetLastError() << std::endl;
40                 return;
41         }
42
43         // make the socket non-blockable if required
44         u_long mode = (u_long)non_blocking;
45         int otp_result = 0;
46
47         otp_result = ioctlsocket(send_socket, FIONBIO, &mode);
48         if (otp_result != NO_ERROR) {
49                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Send socket cannot be set to non blocking mode with error: " << WSAGetLastError() << std::endl;
50         }
51
52         // Create Receive Socket, this socket will be set to unblockable mode by IO channel
53         receive_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
54         recv_address.sin_family = AF_INET;
55         recv_address.sin_addr.s_addr = inet_addr("127.0.0.1");
56         recv_address.sin_port = htons(0);
57         status = bind(receive_socket, (SOCKADDR*)&recv_address,
58                       sizeof(recv_address));
59
60         if (status != 0) {
61                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Receive socket binding failed with error: " << WSAGetLastError() << std::endl;
62                 return;
63         }
64
65         // recieve socket will be made non-blocking by GSource which will use it
66
67         // get assigned port number for Receive Socket
68         int recv_addr_len = sizeof(recv_address);
69         status = getsockname(receive_socket, (SOCKADDR*)&recv_address, &recv_addr_len);
70
71         if (status != 0) {
72                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Setting receive socket address to local failed with error: " << WSAGetLastError() << std::endl;
73                 return;
74         }
75
76         // construct IOChannel
77         receive_channel = g_io_channel_win32_new_socket((gint)receive_socket);
78
79         // set binary data type
80         GIOStatus g_status = g_io_channel_set_encoding (receive_channel, NULL, NULL);
81         if (G_IO_STATUS_NORMAL != g_status ) {
82                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Cannot set flag for IOChannel. " << g_status << std::endl;
83                 return;
84         }
85
86         // disable channel buffering
87         g_io_channel_set_buffered (receive_channel, false);
88 }
89
90 CrossThreadChannel::~CrossThreadChannel ()
91 {
92         /* glibmm hack */
93
94         if (receive_channel) {
95                 g_io_channel_unref (receive_channel);
96         }
97
98         closesocket(send_socket);
99         closesocket(receive_socket);
100 }
101
102 void
103 CrossThreadChannel::wakeup ()
104 {
105         char c = 0;
106
107         // write one byte to wake up a thread which is listening our IOS
108         sendto(send_socket, &c, sizeof(c), 0, (SOCKADDR*)&recv_address, sizeof(recv_address) );
109 }
110
111 void
112 CrossThreadChannel::drain ()
113 {
114         /* flush the buffer - empty the channel from all requests */
115         GError *g_error = 0;
116         gchar buffer[512];
117         gsize read = 0;
118
119         while (1) {
120                 GIOStatus g_status = g_io_channel_read_chars (receive_channel, buffer, sizeof(buffer), &read, &g_error);
121
122                 if (G_IO_STATUS_AGAIN == g_status) {
123                         break;
124                 }
125
126                 if (G_IO_STATUS_NORMAL != g_status) {
127                         std::cerr << "CrossThreadChannel::CrossThreadChannel() Cannot drain from read buffer! " << g_status << std::endl;
128
129                         if (g_error) {
130                                 std::cerr << "Error is Domain: " << g_error->domain << " Code: " << g_error->code << std::endl;
131                                 g_clear_error(&g_error);
132                         } else {
133                                 std::cerr << "No error provided\n";
134                         }
135                         break;
136                 }
137         }
138 }
139
140
141 int
142 CrossThreadChannel::deliver (char msg)
143 {
144
145         // write one particular byte to wake up the thread which is listening our IOS
146         int status = sendto(send_socket, &msg, sizeof(msg), 0, (SOCKADDR*)&recv_address, sizeof(recv_address) );
147
148         if (SOCKET_ERROR  == status) {
149                 return -1;
150         }
151
152         return status;
153 }
154
155 bool
156 CrossThreadChannel::poll_for_request()
157 {
158         // windows before Vista has no poll
159         while(true) {
160                 fd_set rfds;
161                 FD_ZERO(&rfds);
162                 FD_SET(receive_socket, &rfds);
163                 if ((select(receive_socket+1, &rfds, NULL, NULL, NULL)) < 0) {
164                         if (errno == EINTR) {
165                                 continue;
166                         }
167                         break;
168                 }
169                 if(FD_ISSET(receive_socket, &rfds)) {
170                         return true;
171                 }
172         }
173         return false;
174 }
175
176 int
177 CrossThreadChannel::receive (char& msg, bool wait)
178 {
179         gsize read = 0;
180         GError *g_error = 0;
181
182         if (wait) {
183                 if (!poll_for_request ()) {
184                         return -1;
185                 }
186         }
187
188         // fetch the message from the channel.
189         GIOStatus g_status = g_io_channel_read_chars (receive_channel, &msg, sizeof(msg), &read, &g_error);
190
191         if (G_IO_STATUS_NORMAL != g_status) {
192                 read = -1;
193         }
194
195         return read;
196 }