@@ -187,6 +187,9 @@ public class RubyThread extends RubyObject implements ExecutionContext {
187187 /** Thread-local tuple used for sleeping (semaphore, millis, nanos) */
188188 private final SleepTask2 sleepTask = new SleepTask2 ();
189189
190+ /** Thread-local tuple used for waiting (object, millis, nanos) */
191+ private final WaitTask waitTask = new WaitTask ();
192+
190193 /** Whether this is an "adopted" thread not created by Ruby code */
191194 private final boolean adopted ;
192195
@@ -1674,6 +1677,67 @@ public boolean sleep(long milliseconds, long nanoseconds) throws InterruptedExce
16741677 }
16751678 }
16761679
1680+ /**
1681+ * Wait for the object's monitor the specified number of seconds, or forever if seconds is zero.
1682+ *
1683+ * @param o the object to wait for
1684+ * @param seconds the number of seconds to wait, as a double
1685+ * @return whether the wait took at least the specified amount of time, or true if the time specified was zero
1686+ * @throws InterruptedException
1687+ */
1688+ public boolean waitForObject (Object o , double seconds ) throws InterruptedException {
1689+ if (seconds > 0.0 ) {
1690+ long delay_ns = (long )(seconds * 1000000000.0 );
1691+ int delay_ns_remainder = (int )( delay_ns % 1000000 );
1692+ long delay_ms = delay_ns / 1000000 ;
1693+ return waitForObject (o , delay_ms , delay_ns_remainder );
1694+ } else {
1695+ waitForObject (o );
1696+ return true ;
1697+ }
1698+ }
1699+
1700+ /**
1701+ * Wait for the object's monitor until woken up or notified.
1702+ *
1703+ * Equivalent to calling {@link RubyThread#waitForObject(Object, long, int)} with 0 for millis and nanos.
1704+ *
1705+ * @param o the object to wait for
1706+ * @throws InterruptedException
1707+ */
1708+ public void waitForObject (Object o ) throws InterruptedException {
1709+ waitTask .millis = 0 ;
1710+ waitTask .nanos = 0 ;
1711+ executeTaskBlocking (getContext (), o , waitTask );
1712+ }
1713+
1714+ /**
1715+ * Wait for the object's monitor the specified number of milliseconds and nanoseconds, or forever if they are zero.
1716+ *
1717+ * @param o the object to wait for
1718+ * @param millis the number of milliseconds to wait
1719+ * @param nanos the number of nanoseconds to wait
1720+ * @return whether the wait took at least the specified amount of time, or true if the time specified was zero
1721+ * @throws InterruptedException
1722+ */
1723+ public boolean waitForObject (Object o , long millis , int nanos ) throws InterruptedException {
1724+ long totalNanos = millis * 1000000 + nanos ;
1725+ if (totalNanos == 0 ) {
1726+ waitForObject (o );
1727+ return true ;
1728+ }
1729+
1730+ long start_ns = System .nanoTime ();
1731+ if (totalNanos > 0 ) {
1732+ waitTask .millis = millis ;
1733+ waitTask .nanos = nanos ;
1734+ executeTaskBlocking (getContext (), o , waitTask );
1735+ }
1736+ long end_ns = System .nanoTime ();
1737+
1738+ return (end_ns - start_ns ) <= totalNanos ;
1739+ }
1740+
16771741 public IRubyObject status () { // not used
16781742 return status (getRuntime ().getCurrentContext ());
16791743 }
@@ -1801,6 +1865,32 @@ public void wakeup(RubyThread thread, Object data) {
18011865 }
18021866 }
18031867
1868+ /**
1869+ * A Task for waiting on an object's monitor.
1870+ */
1871+ private static class WaitTask implements Task <Object , Long > {
1872+ long millis ;
1873+ int nanos ;
1874+
1875+ @ Override
1876+ public Long run (ThreadContext context , Object data ) throws InterruptedException {
1877+ long start = System .nanoTime ();
1878+
1879+ synchronized (data ) {
1880+ data .wait (millis , nanos );
1881+ }
1882+
1883+ return System .nanoTime () - start ;
1884+ }
1885+
1886+ @ Override
1887+ public void wakeup (RubyThread thread , Object data ) {
1888+ synchronized (data ) {
1889+ data .notify ();
1890+ }
1891+ }
1892+ }
1893+
18041894 @ Deprecated (since = "9.0.0.0" )
18051895 public void executeBlockingTask (BlockingTask task ) throws InterruptedException {
18061896 try {
@@ -2459,23 +2549,12 @@ public void afterBlockingCall() {
24592549 pollThreadEvents ();
24602550 }
24612551
2462- // Deprecated but still used by concurrent_ruby
2552+ /**
2553+ * @deprecated use {@link RubyThread#waitForObject(Object, double)} and siblings
2554+ */
24632555 @ Deprecated (since = "10.1.0.0" )
24642556 public boolean wait_timeout (IRubyObject o , Double timeout ) throws InterruptedException {
2465- if ( timeout != null ) {
2466- long delay_ns = (long )(timeout .doubleValue () * 1000000000.0 );
2467- long start_ns = System .nanoTime ();
2468- if (delay_ns > 0 ) {
2469- long delay_ms = delay_ns / 1000000 ;
2470- int delay_ns_remainder = (int )( delay_ns % 1000000 );
2471- executeBlockingTask (new SleepTask (o , delay_ms , delay_ns_remainder ));
2472- }
2473- long end_ns = System .nanoTime ();
2474- return ( end_ns - start_ns ) <= delay_ns ;
2475- } else {
2476- executeBlockingTask (new SleepTask (o , 0 , 0 ));
2477- return true ;
2478- }
2557+ return waitForObject (o , timeout == null ? 0 : timeout .doubleValue ());
24792558 }
24802559
24812560 public RubyThreadGroup getThreadGroup () {
0 commit comments