Remove unusued API Create/Delete Binding
[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         if (receive_source) {
93                 /* this disconnects it from any main context it was attached in
94                    in ::attach(), this prevent its callback from being invoked
95                    after the destructor has finished.
96                 */
97                 g_source_destroy (receive_source);
98         }
99
100         /* glibmm hack */
101
102         if (receive_channel) {
103                 g_io_channel_unref (receive_channel);
104         }
105
106         closesocket(send_socket);
107         closesocket(receive_socket);
108 }
109
110 void
111 CrossThreadChannel::wakeup ()
112 {
113         char c = 0;
114
115         // write one byte to wake up a thread which is listening our IOS
116         sendto(send_socket, &c, sizeof(c), 0, (SOCKADDR*)&recv_address, sizeof(recv_address) );
117 }
118
119 void
120 CrossThreadChannel::drain ()
121 {
122         /* flush the buffer - empty the channel from all requests */
123         GError *g_error = 0;
124         gchar buffer[512];
125         gsize read = 0;
126
127         while (1) {
128                 GIOStatus g_status = g_io_channel_read_chars (receive_channel, buffer, sizeof(buffer), &read, &g_error);
129
130                 if (G_IO_STATUS_AGAIN == g_status) {
131                         break;
132                 }
133
134                 if (G_IO_STATUS_NORMAL != g_status) {
135                         std::cerr << "CrossThreadChannel::CrossThreadChannel() Cannot drain from read buffer! " << g_status << std::endl;
136
137                         if (g_error) {
138                                 std::cerr << "Error is Domain: " << g_error->domain << " Code: " << g_error->code << std::endl;
139                                 g_clear_error(&g_error);
140                         } else {
141                                 std::cerr << "No error provided\n";
142                         }
143                         break;
144                 }
145         }
146 }
147
148
149 int
150 CrossThreadChannel::deliver (char msg)
151 {
152
153         // write one particular byte to wake up the thread which is listening our IOS
154         int status = sendto(send_socket, &msg, sizeof(msg), 0, (SOCKADDR*)&recv_address, sizeof(recv_address) );
155
156         if (SOCKET_ERROR  == status) {
157                 return -1;
158         }
159
160         return status;
161 }
162
163 bool
164 CrossThreadChannel::poll_for_request()
165 {
166         // windows before Vista has no poll
167         while(true) {
168                 fd_set rfds;
169                 FD_ZERO(&rfds);
170                 FD_SET(receive_socket, &rfds);
171                 if ((select(receive_socket+1, &rfds, NULL, NULL, NULL)) < 0) {
172                         if (errno == EINTR) {
173                                 continue;
174                         }
175                         break;
176                 }
177                 if(FD_ISSET(receive_socket, &rfds)) {
178                         return true;
179                 }
180         }
181         return false;
182 }
183
184 int
185 CrossThreadChannel::receive (char& msg, bool wait)
186 {
187         gsize read = 0;
188         GError *g_error = 0;
189
190         if (wait) {
191                 if (!poll_for_request ()) {
192                         return -1;
193                 }
194         }
195
196         // fetch the message from the channel.
197         GIOStatus g_status = g_io_channel_read_chars (receive_channel, &msg, sizeof(msg), &read, &g_error);
198
199         if (G_IO_STATUS_NORMAL != g_status) {
200                 read = -1;
201         }
202
203         return read;
204 }