1515
1616import java .lang .foreign .Arena ;
1717import java .lang .foreign .MemorySegment ;
18- import java .lang .foreign .SegmentAllocator ;
19- import java .lang .foreign .SegmentScope ;
2018import java .util .ArrayList ;
21- import java .util .HashMap ;
19+ import java .util .LinkedHashMap ;
2220import java .util .List ;
2321import java .util .Map ;
2422
@@ -58,15 +56,15 @@ static synchronized LinuxAsyncTask instance() {
5856 return singletonInstance ;
5957 }
6058
61- private final SegmentAllocator GLOBAL_ALLOCATOR = SegmentAllocator . nativeAllocator ( SegmentScope . global () );
59+ private final Arena urbArena = Arena . openShared ( );
6260 /// available URBs
6361 private final List <MemorySegment > availableURBs = new ArrayList <>();
6462 /// map of URB addresses to transfer (for outstanding transfers)
65- private final Map <Long , LinuxTransfer > transfersByURB = new HashMap <>();
63+ private final Map <MemorySegment , LinuxTransfer > transfersByURB = new LinkedHashMap <>();
6664 /// array of file descriptors using asynchronous completion
6765 private int [] asyncFds ;
6866 /// file descriptor to notify async IO background thread about an update
69- private int asyncIOUpdateEventFd ;
67+ private int asyncIOWakeUpEventFd ;
7068
7169 /**
7270 * Background task for handling asynchronous IO completions.
@@ -103,7 +101,8 @@ private void asyncCompletionTask() {
103101 pollfd .revents$set (asyncPolls , i , (short ) 0 );
104102 }
105103
106- pollfd .fd$set (asyncPolls , n , asyncIOUpdateEventFd );
104+ // entry n is the wake-up event file descriptor
105+ pollfd .fd$set (asyncPolls , n , asyncIOWakeUpEventFd );
107106 pollfd .events$set (asyncPolls , n , (short ) poll .POLLIN ());
108107 pollfd .revents$set (asyncPolls , n , (short ) 0 );
109108
@@ -112,74 +111,79 @@ private void asyncCompletionTask() {
112111 if (res < 0 )
113112 throwException ("internal error (poll)" );
114113
115- // check for events
116- for (int i = 0 ; i < n + 1 ; i ++) {
117- var revent = pollfd .revents$get (asyncPolls , i );
118- if (revent == 0 )
119- continue ;
114+ // acquire lock
115+ synchronized (this ) {
120116
121- if ((revent & poll .POLLERR ()) != 0 ) {
122- // most likely the device has been disconnected;
123- // remove from polled FD list to prevent further problems
124- int fd = pollfd . fd$get ( asyncPolls , i );
125- removeFdFromAsyncIOCompletion ( fd );
117+ if ((pollfd . revents$get ( asyncPolls , n ) & poll .POLLIN ()) != 0 ) {
118+ // wakeup to refresh list of file descriptors
119+ res = IO . eventfd_read ( asyncIOWakeUpEventFd , eventfdValueHolder , errnoState );
120+ if ( res < 0 )
121+ throwLastError ( errnoState , "internal error (eventfd_read)" );
126122 continue ;
127123 }
128124
129- if (i != n ) {
125+ // check for events
126+ for (int i = 0 ; i < n + 1 ; i ++) {
127+ var revent = pollfd .revents$get (asyncPolls , i );
128+ if (revent == 0 )
129+ continue ;
130+
131+ if ((revent & poll .POLLERR ()) != 0 ) {
132+ // most likely the device has been disconnected;
133+ // remove from polled FD list to prevent further problems
134+ int fd = pollfd .fd$get (asyncPolls , i );
135+ removeFdFromAsyncIOCompletion (fd );
136+ continue ;
137+ }
138+
130139 // reap URB
131140 int fd = pollfd .fd$get (asyncPolls , i );
132- reapURB (fd , urbPointerHolder , errnoState );
133-
134- } else {
135- // wakeup to refresh list of file descriptors
136- res = IO .eventfd_read (asyncIOUpdateEventFd , eventfdValueHolder , errnoState );
137- if (res < 0 )
138- throwLastError (errnoState , "internal error (eventfd_read)" );
141+ reapURBs (fd , urbPointerHolder , errnoState );
139142 }
140143 }
141144 }
142145 }
143146 }
144147
145148 /**
146- * Reap URB and handle the completed transfer .
149+ * Reap all pending URBs and handle the completed transfers .
147150 *
148151 * @param fd file descriptor
149152 * @param urbPointerHolder native memory to receive the URB pointer
150153 * @param errnoState native memory to receive the errno
151154 */
152- private void reapURB (int fd , MemorySegment urbPointerHolder , MemorySegment errnoState ) {
153- int res ;
154- res = IO .ioctl (fd , REAPURBNDELAY , urbPointerHolder , errnoState );
155- if (res < 0 ) {
156- var err = Linux .getErrno (errnoState );
157- if (err == errno .EAGAIN ())
158- return ; // retry
159- if (err == errno .EBADF ())
160- return ; // ignore, device might have been closed
161- throwException (err , "internal error (reap URB)" );
162- }
155+ private void reapURBs (int fd , MemorySegment urbPointerHolder , MemorySegment errnoState ) {
156+ while ( true ) {
157+ int res = IO .ioctl (fd , REAPURBNDELAY , urbPointerHolder , errnoState );
158+ if (res < 0 ) {
159+ var err = Linux .getErrno (errnoState );
160+ if (err == errno .EAGAIN ())
161+ return ; // no more pending URBs
162+ if (err == errno .EBADF ())
163+ return ; // ignore, device might have been closed
164+ throwException (err , "internal error (reap URB)" );
165+ }
163166
164- // call completion handler
165- var urbAddr = urbPointerHolder .get (JAVA_LONG , 0 );
166- var transfer = getTransferResult (urbAddr );
167- transfer .completion .completed (transfer );
167+ // call completion handler
168+ var urb = urbPointerHolder .get (ADDRESS , 0 );
169+ var transfer = getTransferResult (urb );
170+ transfer .completion .completed (transfer );
171+ }
168172 }
169173
170174 /**
171175 * Notifies background process about changed FD list
172176 */
173177 private void notifyAsyncIOTask () {
174178 // start background process if needed
175- if (asyncIOUpdateEventFd == 0 ) {
179+ if (asyncIOWakeUpEventFd == 0 ) {
176180 startAsyncIOTask ();
177181 return ;
178182 }
179183
180184 try (var arena = Arena .openConfined ()) {
181185 var errnoState = arena .allocate (Linux .ERRNO_STATE .layout ());
182- if (IO .eventfd_write (asyncIOUpdateEventFd , 1 , errnoState ) < 0 )
186+ if (IO .eventfd_write (asyncIOWakeUpEventFd , 1 , errnoState ) < 0 )
183187 throwLastError (errnoState , "internal error (eventfd_write)" );
184188 }
185189 }
@@ -268,15 +272,15 @@ private void addURB(LinuxTransfer transfer) {
268272 if (size > 0 ) {
269273 urb = availableURBs .remove (size - 1 );
270274 } else {
271- urb = usbdevfs_urb .allocate (GLOBAL_ALLOCATOR );
275+ urb = usbdevfs_urb .allocate (urbArena );
272276 }
273277
274278 transfer .urb = urb ;
275- transfersByURB .put (urb . address () , transfer );
279+ transfersByURB .put (urb , transfer );
276280 }
277281
278- private synchronized LinuxTransfer getTransferResult (long urbAddr ) {
279- var transfer = transfersByURB .remove (urbAddr );
282+ private synchronized LinuxTransfer getTransferResult (MemorySegment urb ) {
283+ var transfer = transfersByURB .remove (urb );
280284 if (transfer == null )
281285 throwException ("internal error (unknown URB)" );
282286
@@ -291,27 +295,31 @@ private synchronized LinuxTransfer getTransferResult(long urbAddr) {
291295 synchronized void abortTransfers (LinuxUSBDevice device , byte endpointAddress ) {
292296 int fd = device .fileDescriptor ();
293297 try (var arena = Arena .openConfined ()) {
298+
294299 var errnoState = arena .allocate (Linux .ERRNO_STATE .layout ());
295300
296- for ( var urbAddress : transfersByURB . keySet ()) {
297- var urb = usbdevfs_urb . ofAddress ( MemorySegment . ofAddress ( urbAddress ), SegmentScope . global ());
301+ // iterate all URBs and discard the ones for the specified endpoint
302+ for ( var urb : transfersByURB . keySet ()) {
298303 if (fd != (int ) usbdevfs_urb .usercontext$get (urb ).address ())
299304 continue ;
300305 if (endpointAddress != usbdevfs_urb .endpoint$get (urb ))
301306 continue ;
302307
303- if (IO .ioctl (fd , DISCARDURB , urb , errnoState ) < 0 )
304- throwLastError (errnoState , "failed to cancel transfer" );
308+ if (IO .ioctl (fd , DISCARDURB , urb , errnoState ) < 0 ) {
309+ // ignore EINVAL; it occurs if the URB has completed at the same time
310+ if (Linux .getErrno (errnoState ) != errno .EINVAL ())
311+ throwLastError (errnoState , "failed to abort transfer" );
312+ }
305313 }
306314 }
307315 }
308316
309317 private void startAsyncIOTask () {
310318 try (var arena = Arena .openConfined ()) {
311319 var errnoState = arena .allocate (Linux .ERRNO_STATE .layout ());
312- asyncIOUpdateEventFd = IO .eventfd (0 , 0 , errnoState );
313- if (asyncIOUpdateEventFd == -1 ) {
314- asyncIOUpdateEventFd = 0 ;
320+ asyncIOWakeUpEventFd = IO .eventfd (0 , 0 , errnoState );
321+ if (asyncIOWakeUpEventFd == -1 ) {
322+ asyncIOWakeUpEventFd = 0 ;
315323 throwLastError (errnoState , "internal error (eventfd)" );
316324 }
317325 }
0 commit comments