ardour
crossthread.win.cc
Go to the documentation of this file.
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 
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 
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
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
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
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
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 }
int receive(char &msg, bool wait=false)
CrossThreadChannel(bool non_blocking)
GIOChannel * receive_channel
Definition: crossthread.h:91
LIBARDOUR_API PBD::PropertyDescriptor< bool > select
Definition: route_group.cc:48