Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /base /sync_socket_win.cc
blob: 77bc8e15b180532b4d20cd33275155bd9d41a7d9 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:06[diff] [blame]1// Copyright 2012 The Chromium Authors
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include"base/sync_socket.h"
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]6
avi9b6f42932015-12-26 22:15:14[diff] [blame]7#include<limits.h>
8#include<stddef.h>
9
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]10#include<utility>
11
Adam Riced47f4982024-09-03 16:18:23[diff] [blame]12#include"base/check.h"
Tom Sepez978847c2025-03-22 03:43:59[diff] [blame]13#include"base/compiler_specific.h"
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]14#include"base/containers/span.h"
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]15#include"base/logging.h"
Peter Boström6e2fd082024-01-23 01:17:55[diff] [blame]16#include"base/notimplemented.h"
wfhcb35f4e72015-09-22 18:10:11[diff] [blame]17#include"base/rand_util.h"
Etienne Pierre-Doray3879b052018-09-17 14:17:22[diff] [blame]18#include"base/threading/scoped_blocking_call.h"
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]19#include"base/win/scoped_handle.h"
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]20
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]21namespace base{
22
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]23using win::ScopedHandle;
24
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]25namespace{
cpu@chromium.org774bdcce2012-01-19 03:44:38[diff] [blame]26// IMPORTANT: do not change how this name is generated because it will break
27// in sandboxed scenarios as we might have by-name policies that allow pipe
28// creation. Also keep the secure random number generation.
29constwchar_t kPipeNameFormat[]= L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu";
Daniel Chengf45f47602022-02-28 22:38:32[diff] [blame]30constsize_t kPipePathMax= std::size(kPipeNameFormat)+(3*10)+1;
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]31
32// To avoid users sending negative message lengths to Send/Receive
33// we clamp message lengths, which are size_t, to no more than INT_MAX.
34constsize_t kMaxMessageLength=static_cast<size_t>(INT_MAX);
35
36constint kOutBufferSize=4096;
37constint kInBufferSize=4096;
38constint kDefaultTimeoutMilliSeconds=1000;
39
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]40boolCreatePairImpl(ScopedHandle* socket_a,
41ScopedHandle* socket_b,
42bool overlapped){
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]43 DCHECK_NE(socket_a, socket_b);
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]44 DCHECK(!socket_a->is_valid());
45 DCHECK(!socket_b->is_valid());
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]46
47wchar_t name[kPipePathMax];
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]48ScopedHandle handle_a;
49 DWORD flags= PIPE_ACCESS_DUPLEX| FILE_FLAG_FIRST_PIPE_INSTANCE;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]50if(overlapped){
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]51 flags|= FILE_FLAG_OVERLAPPED;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]52}
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]53
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]54do{
wfhcb35f4e72015-09-22 18:10:11[diff] [blame]55unsignedlong rnd_name;
danakj95305d272024-05-09 20:38:44[diff] [blame]56RandBytes(byte_span_from_ref(rnd_name));
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]57
Tom Sepez978847c2025-03-22 03:43:59[diff] [blame]58 UNSAFE_TODO(swprintf(name, kPipePathMax, kPipeNameFormat,
59GetCurrentProcessId(),GetCurrentThreadId(),
60 rnd_name));
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]61
62 handle_a.Set(CreateNamedPipeW(
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]63 name, flags, PIPE_TYPE_BYTE| PIPE_READMODE_BYTE,1, kOutBufferSize,
64 kInBufferSize, kDefaultTimeoutMilliSeconds, NULL));
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]65}while(!handle_a.is_valid()&&(GetLastError()== ERROR_PIPE_BUSY));
cpu@chromium.org774bdcce2012-01-19 03:44:38[diff] [blame]66
Adam Riced47f4982024-09-03 16:18:23[diff] [blame]67 CHECK(handle_a.is_valid());
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]68
69// The SECURITY_ANONYMOUS flag means that the server side (handle_a) cannot
70// impersonate the client (handle_b). This allows us not to care which side
cpu@chromium.org774bdcce2012-01-19 03:44:38[diff] [blame]71// ends up in which side of a privilege boundary.
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]72 flags= SECURITY_SQOS_PRESENT| SECURITY_ANONYMOUS;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]73if(overlapped){
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]74 flags|= FILE_FLAG_OVERLAPPED;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]75}
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]76
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]77ScopedHandle handle_b(CreateFileW(name, GENERIC_READ| GENERIC_WRITE,
780,// no sharing.
79 NULL,// default security attributes.
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]80 OPEN_EXISTING,// opens existing pipe.
81 flags,
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]82 NULL));// no template file.
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]83if(!handle_b.is_valid()){
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]84 DPLOG(ERROR)<<"CreateFileW failed";
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]85returnfalse;
86}
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]87
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]88if(!ConnectNamedPipe(handle_a.get(), NULL)){
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]89 DWORD error=GetLastError();
90if(error!= ERROR_PIPE_CONNECTED){
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]91 DPLOG(ERROR)<<"ConnectNamedPipe failed";
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]92returnfalse;
93}
94}
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]95
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]96*socket_a= std::move(handle_a);
97*socket_b= std::move(handle_b);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]98
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]99returntrue;
100}
101
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]102// Inline helper to avoid having the cast everywhere.
103DWORDGetNextChunkSize(size_t current_pos,size_t max_size){
104// The following statement is for 64 bit portability.
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]105returnstatic_cast<DWORD>(((max_size- current_pos)<= UINT_MAX)
106?(max_size- current_pos)
107: UINT_MAX);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]108}
109
110// Template function that supports calling ReadFile or WriteFile in an
111// overlapped fashion and waits for IO completion. The function also waits
112// on an event that can be used to cancel the operation. If the operation
113// is cancelled, the function returns and closes the relevant socket object.
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]114template<typenameDataType,typenameFunction>
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]115size_tCancelableFileOperation(Function operation,
116 HANDLE file,
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]117 span<DataType> buffer,
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]118WaitableEvent* io_event,
119WaitableEvent* cancel_event,
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]120CancelableSyncSocket* socket,
121 DWORD timeout_in_ms){
Etienne Bergeron436d42212019-02-26 17:15:12[diff] [blame]122ScopedBlockingCall scoped_blocking_call(FROM_HERE,BlockingType::MAY_BLOCK);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]123// The buffer must be byte size or the length check won't make much sense.
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]124static_assert(sizeof(DataType)==1u,"incorrect buffer type");
125 CHECK(!buffer.empty());
126 CHECK_LE(buffer.size(), kMaxMessageLength);
127 CHECK_NE(file,SyncSocket::kInvalidHandle);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]128
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]129// Track the finish time so we can calculate the timeout as data is read.
130TimeTicks current_time, finish_time;
131if(timeout_in_ms!= INFINITE){
132 current_time=TimeTicks::Now();
Peter Kastinge5a38ed2021-10-02 03:06:35[diff] [blame]133 finish_time= current_time+ base::Milliseconds(timeout_in_ms);
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]134}
135
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]136size_t count=0;
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]137do{
138// The OVERLAPPED structure will be modified by ReadFile or WriteFile.
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]139 OVERLAPPED ol={0};
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]140 ol.hEvent= io_event->handle();
141
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]142const DWORD chunk_size=GetNextChunkSize(count, buffer.size());
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]143// This is either the ReadFile or WriteFile call depending on whether
144// we're receiving or sending data.
tommi@chromium.org23bf6982012-02-03 12:44:44[diff] [blame]145 DWORD len=0;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]146auto operation_buffer= buffer.subspan(count, chunk_size);
147// SAFETY: The below static_cast is in range for DWORD because
148// `operation_buffer` is constructed with a DWORD length above from
149// `chunk_size`.
150const BOOL operation_ok=
151 operation(file, operation_buffer.data(),
152static_cast<DWORD>(operation_buffer.size()),&len,&ol);
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]153if(!operation_ok){
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]154if(::GetLastError()== ERROR_IO_PENDING){
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]155 HANDLE events[]={io_event->handle(), cancel_event->handle()};
Peter Kasting28b51cf2022-06-28 15:02:43[diff] [blame]156const DWORD wait_result=WaitForMultipleObjects(
Daniel Chengf45f47602022-02-28 22:38:32[diff] [blame]157 std::size(events), events, FALSE,
Avi Drissmane3b70bf2019-01-04 19:50:22[diff] [blame]158 timeout_in_ms== INFINITE
159? timeout_in_ms
160:static_cast<DWORD>(
161(finish_time- current_time).InMilliseconds()));
dalecurtis3bdbb0162014-09-16 00:41:22[diff] [blame]162if(wait_result!= WAIT_OBJECT_0+0){
163// CancelIo() doesn't synchronously cancel outstanding IO, only marks
164// outstanding IO for cancellation. We must call GetOverlappedResult()
165// below to ensure in flight writes complete before returning.
tommi@chromium.org23bf6982012-02-03 12:44:44[diff] [blame]166CancelIo(file);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]167}
dalecurtis3bdbb0162014-09-16 00:41:22[diff] [blame]168
169// We set the |bWait| parameter to TRUE for GetOverlappedResult() to
170// ensure writes are complete before returning.
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]171if(!GetOverlappedResult(file,&ol,&len, TRUE)){
dalecurtis3bdbb0162014-09-16 00:41:22[diff] [blame]172 len=0;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]173}
dalecurtis3bdbb0162014-09-16 00:41:22[diff] [blame]174
175if(wait_result== WAIT_OBJECT_0+1){
176 DVLOG(1)<<"Shutdown was signaled. Closing socket.";
177 socket->Close();
178return count;
179}
180
181// Timeouts will be handled by the while() condition below since
182// GetOverlappedResult() may complete successfully after CancelIo().
183 DCHECK(wait_result== WAIT_OBJECT_0+0|| wait_result== WAIT_TIMEOUT);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]184}else{
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]185break;
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]186}
187}
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]188
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]189 count+= len;
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]190
191// Quit the operation if we can't write/read anymore.
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]192if(len!= chunk_size){
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]193break;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]194}
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]195
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]196// Since TimeTicks::Now() is expensive, only bother updating the time if we
197// have more work to do.
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]198if(timeout_in_ms!= INFINITE&& count< buffer.size()){
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]199 current_time= base::TimeTicks::Now();
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]200}
201}while(count< buffer.size()&&
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]202(timeout_in_ms== INFINITE|| current_time< finish_time));
203
204return count;
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]205}
206
207}// namespace
208
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]209// static
210boolSyncSocket::CreatePair(SyncSocket* socket_a,SyncSocket* socket_b){
211returnCreatePairImpl(&socket_a->handle_,&socket_b->handle_,false);
212}
213
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]214voidSyncSocket::Close(){
215 handle_.Close();
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]216}
217
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]218size_tSyncSocket::Send(span<constuint8_t> data){
Etienne Bergeron436d42212019-02-26 17:15:12[diff] [blame]219ScopedBlockingCall scoped_blocking_call(FROM_HERE,BlockingType::MAY_BLOCK);
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]220 CHECK_LE(data.size(), kMaxMessageLength);
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]221 DCHECK(IsValid());
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]222size_t count=0;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]223while(count< data.size()){
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]224 DWORD len;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]225const DWORD chunk_size=GetNextChunkSize(count, data.size());
226auto data_chunk= data.subspan(count, chunk_size);
227// SAFETY: The below static_cast is in range for DWORD because `data_chunk`
228// is constructed with a DWORD length above from `chunk_size`.
229if(::WriteFile(handle(), data_chunk.data(),
230static_cast<DWORD>(data_chunk.size()),&len,
231 NULL)== FALSE){
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]232return count;
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]233}
234 count+= len;
235}
236return count;
237}
238
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]239size_tSyncSocket::ReceiveWithTimeout(span<uint8_t> buffer,TimeDelta timeout){
240 NOTIMPLEMENTED();
241return0;
242}
243
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]244size_tSyncSocket::Receive(span<uint8_t> buffer){
Etienne Bergeron436d42212019-02-26 17:15:12[diff] [blame]245ScopedBlockingCall scoped_blocking_call(FROM_HERE,BlockingType::MAY_BLOCK);
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]246 CHECK_LE(buffer.size(), kMaxMessageLength);
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]247 DCHECK(IsValid());
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]248size_t count=0;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]249while(count< buffer.size()){
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]250 DWORD len;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]251const DWORD chunk_size=GetNextChunkSize(count, buffer.size());
252auto data_chunk= buffer.subspan(count, chunk_size);
253// SAFETY: The below static_cast is in range for DWORD because `data_chunk`
254// is constructed with a DWORD length above from `chunk_size`.
255if(::ReadFile(handle(), data_chunk.data(),
256static_cast<DWORD>(data_chunk.size()),&len,
Nico Weber2499aee2017-10-17 20:56:49[diff] [blame]257 NULL)== FALSE){
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]258return count;
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]259}
260 count+= len;
261}
262return count;
263}
264
cpu@chromium.orgd8b65912009-12-04 22:53:22[diff] [blame]265size_tSyncSocket::Peek(){
266 DWORD available=0;
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]267PeekNamedPipe(handle(), NULL,0, NULL,&available, NULL);
cpu@chromium.orgd8b65912009-12-04 22:53:22[diff] [blame]268return available;
269}
270
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]271boolSyncSocket::IsValid()const{
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]272return handle_.is_valid();
maxmorind4bcb112017-04-13 11:43:13[diff] [blame]273}
274
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]275SyncSocket::HandleSyncSocket::handle()const{
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]276return handle_.get();
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]277}
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]278
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]279SyncSocket::HandleSyncSocket::Release(){
Lei Zhang04c0bcb2022-02-04 04:13:45[diff] [blame]280return handle_.release();
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]281}
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]282
283boolCancelableSyncSocket::Shutdown(){
284// This doesn't shut down the pipe immediately, but subsequent Receive or Send
285// methods will fail straight away.
286 shutdown_event_.Signal();
287returntrue;
288}
289
Robert Sesek6ab73b022020-02-13 16:42:39[diff] [blame]290voidCancelableSyncSocket::Close(){
291SyncSocket::Close();
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]292 shutdown_event_.Reset();
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]293}
294
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]295size_tCancelableSyncSocket::Send(span<constuint8_t> data){
xians@chromium.org5d272092012-04-19 10:23:03[diff] [blame]296staticconst DWORD kWaitTimeOutInMs=500;
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]297returnCancelableFileOperation(&::WriteFile, handle(), data,&file_operation_,
298&shutdown_event_,this, kWaitTimeOutInMs);
299}
300
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]301size_tCancelableSyncSocket::Receive(span<uint8_t> buffer){
302returnCancelableFileOperation(&::ReadFile, handle(), buffer,
303&file_operation_,&shutdown_event_,this,
304 INFINITE);
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]305}
306
Austin Sullivanedf168fd2024-01-17 21:37:20[diff] [blame]307size_tCancelableSyncSocket::ReceiveWithTimeout(span<uint8_t> buffer,
308TimeDelta timeout){
309returnCancelableFileOperation(&::ReadFile, handle(), buffer,
310&file_operation_,&shutdown_event_,this,
311static_cast<DWORD>(timeout.InMilliseconds()));
dalecurtis@chromium.org62558f12013-10-19 22:13:19[diff] [blame]312}
313
tommi@chromium.org532e9bd2012-01-25 12:04:17[diff] [blame]314// static
315boolCancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
316CancelableSyncSocket* socket_b){
317returnCreatePairImpl(&socket_a->handle_,&socket_b->handle_,true);
318}
319
sehr@google.com0840cc72009-11-24 16:14:53[diff] [blame]320}// namespace base

[8]ページ先頭

©2009-2025 Movatter.jp