clean up whitespace
[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         WSADATA wsaData;
29
30         if(WSAStartup(MAKEWORD(1,1),&wsaData) != 0) {
31                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Winsock initialization failed with error: " << WSAGetLastError() << std::endl;
32                 return;
33         }
34
35         struct sockaddr_in send_address;
36
37         // Create Send Socket
38         send_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
39         send_address.sin_family = AF_INET;
40         send_address.sin_addr.s_addr = inet_addr("127.0.0.1");
41         send_address.sin_port = htons(0);
42         int status = bind(send_socket, (SOCKADDR*)&send_address,   
43                           sizeof(send_address));
44
45         if (status != 0) {
46                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Send socket binding failed with error: " << WSAGetLastError() << std::endl;
47                 return;
48         }
49
50         // make the socket non-blockable if required
51         u_long mode = (u_long)non_blocking;
52         int otp_result = 0;
53         
54         otp_result = ioctlsocket(send_socket, FIONBIO, &mode);
55         if (otp_result != NO_ERROR) {
56                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Send socket cannot be set to non blocking mode with error: " << WSAGetLastError() << std::endl;
57         }
58
59         // Create Receive Socket, this socket will be set to unblockable mode by IO channel
60         receive_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
61         recv_address.sin_family = AF_INET;
62         recv_address.sin_addr.s_addr = inet_addr("127.0.0.1");
63         recv_address.sin_port = htons(0);
64         status = bind(receive_socket, (SOCKADDR*)&recv_address, 
65                       sizeof(recv_address));
66     
67         if (status != 0) {
68                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Receive socket binding failed with error: " << WSAGetLastError() << std::endl;
69                 return;
70         }
71
72         // recieve socket will be made non-blocking by GSource which will use it
73
74         // get assigned port number for Receive Socket
75         int recv_addr_len = sizeof(recv_address);
76         status = getsockname(receive_socket, (SOCKADDR*)&recv_address, &recv_addr_len);
77     
78         if (status != 0) {
79                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Setting receive socket address to local failed with error: " << WSAGetLastError() << std::endl;
80                 return;
81         }
82         
83         // construct IOChannel
84         receive_channel = g_io_channel_win32_new_socket((gint)receive_socket);
85         
86         // set binary data type
87         GIOStatus g_status = g_io_channel_set_encoding (receive_channel, NULL, NULL);
88         if (G_IO_STATUS_NORMAL != g_status ) {
89                 std::cerr << "CrossThreadChannel::CrossThreadChannel() Cannot set flag for IOChannel. " << g_status << std::endl;
90                 return;
91         }
92
93         // disable channel buffering
94         g_io_channel_set_buffered (receive_channel, false);
95 }
96
97 CrossThreadChannel::~CrossThreadChannel ()
98 {
99         /* glibmm hack */
100
101         if (receive_channel) {
102                 g_io_channel_unref (receive_channel);
103         }
104
105         closesocket(send_socket);
106         closesocket(receive_socket);
107         WSACleanup();
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 int 
164 CrossThreadChannel::receive (char& msg)
165 {
166         gsize read = 0;
167         GError *g_error = 0;
168         
169         // fetch the message from the channel.
170         GIOStatus g_status = g_io_channel_read_chars (receive_channel, &msg, sizeof(msg), &read, &g_error);
171
172         if (G_IO_STATUS_NORMAL != g_status) {
173                 read = -1;
174         }
175
176         return read;
177 }