Project

General

Profile

Feature #4182

KERNEL32:SLEEP() emulation

Added by Greg Shah almost 5 years ago. Updated over 3 years ago.

Status:
Closed
Priority:
Normal
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
version_reported:
version_resolved:

History

#1 Updated by Greg Shah almost 5 years ago

This is a trivial implementation of millisecond level sleep using Java's Thread.sleep().

#2 Updated by Roger Borrello over 4 years ago

Yes... the java is quite trivial. But I want to make sure to integrate properly.

I've updated NativeAPIEmulation.java with sleep within the kernel32 class. The 4GL syntax is:

PROCEDURE Sleep EXTERNAL "KERNEL32.DLL":
  DEFINE INPUT PARAMETER intMilliseconds AS LONG.
END PROCEDURE.

Will we be adding an os-sleep (or similar) FWD extension for the code to use? Or is there already a conversion for external "kernel32.dll" in place?

#3 Updated by Greg Shah over 4 years ago

Conversion is automatic (see rules/annotations/native_api.rules and rules/convert/native_api.rules). The conversion calculates and emits the names based on the implementation in NativeAPIEmulation.

Just implement the method correctly (with the right method name and signature) and nothing else is needed.

#4 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

Conversion is automatic (see rules/annotations/native_api.rules and rules/convert/native_api.rules). The conversion calculates and emits the names based on the implementation in NativeAPIEmulation.

Just implement the method correctly (with the right method name and signature) and nothing else is needed.

Within the kernel32 class:

         /**
          * Sleep for the duration passed in.
          *
          * @param   dwMilliseconds
          *          Number of milliseconds to sleep.
          *
          * @return  none.
          */
         public static void sleep(long dwMilliseconds)
         {
            try
            {
               Thread.sleep(dwMilliseconds);
            }
            catch (InterruptedException ie)
            {
               // suppress
            }
         }  

#5 Updated by Greg Shah over 4 years ago

Yes, that is the idea. Except there are additional things to consider:

  • The parameter type rec'd will be one of the BDTs (e.g. integer) instead of long.
  • If a negative number is passed, Java will raise IllegalArgumentException which would be bad. Either we check the input for non-negative and return immediately, or we need to handle this exception. If kernel32:sleep sets errno, perhaps we should as well.
  • Let's name it millis instead of dwMilliseconds.
  • I suspect that in the 4GL, if there is an interruption it may be translated into a 4GL STOP condition instead of ignored. Check this (write a testcase that sleeps for a long number of millis and then interrupt the thread, probably with a signal).

#6 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

  • I suspect that in the 4GL, if there is an interruption it may be translated into a 4GL STOP condition instead of ignored. Check this (write a testcase that sleeps for a long number of millis and then interrupt the thread, probably with a signal).

I'm not so sure that is the case. I have this testcase, and we don't end up leaving early in 4GL. After we return from the 10 seconds sleep, the program exits.

def var duration as integer no-undo initial 10000.
def var i as integer no-undo initial 0.

display "Press spacebar to sleep...".
pause.

do while i < 2 on stop undo, leave:
   run Sleep (duration).
   message "Slept for " + string(duration) + " milliseconds".
   i = i + 1.
end.
message "i = " + string(i).               
message "Done.".

procedure Sleep external "KERNEL32.DLL":
  define input parameter intMilliseconds as long.
end procedure.
  • The parameter type rec'd will be one of the BDTs (e.g. integer) instead of long.

The customer code is calling Sleep using inMilliseconds, which is a long. I am using integer:

         public static void sleep(integer millis)
         {
            try
            {
               if (millis > 0)
                  Thread.sleep(millis);
            }
            catch (InterruptedException ie)
            {
               // suppress
            }
         }  

I get an incompatible error:

    [javac] /home/rfb/projects/testcases/src/com/goldencode/testcases/windows_emulation/Kernel32Sleep.java:72: error: incompatible types: integer cannot be converted to long
    [javac]          NativeAPIEmulation.windows.kernel32.sleep(intMilliseconds);

#7 Updated by Greg Shah over 4 years ago

I have this testcase, and we don't end up leaving early in 4GL. After we return from the 10 seconds sleep, the program exits.

Did you signal/interrupt the process?

which is a long

Use int64.

#8 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

I have this testcase, and we don't end up leaving early in 4GL. After we return from the 10 seconds sleep, the program exits.

Did you signal/interrupt the process?

I pressed <ctrl+break> before the initial pause, and the program exits immediately. I pressed <ctrl+break> after pressing <space> and nothing happens for 10 seconds, then the program immediately exits back to the procedure editor. If I just wait 20 seconds, the display shows the "i = 2" message and "Done" at the bottom.

#9 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

which is a long

Use int64.

In the 4GL or Java?

#10 Updated by Greg Shah over 4 years ago

In Java. int64 is the BDT class that will be passed instead of a long.

#11 Updated by Greg Shah over 4 years ago

Roger Borrello wrote:

Greg Shah wrote:

I have this testcase, and we don't end up leaving early in 4GL. After we return from the 10 seconds sleep, the program exits.

Did you signal/interrupt the process?

I pressed <ctrl+break> before the initial pause, and the program exits immediately. I pressed <ctrl+break> after pressing <space> and nothing happens for 10 seconds, then the program immediately exits back to the procedure editor. If I just wait 20 seconds, the display shows the "i = 2" message and "Done" at the bottom.

This seems like it is the 4GL eating the ctrl-break. See if there is a way to send a signal that the kernel32 code will "see".

#12 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

In Java. int64 is the BDT class that will be passed instead of a long.

If we want to be compatible with WinSDK, in which kernel32 sleep accepts a DWORD (unsigned 32-bit), should we just mask out the upper 32-bits of int64 into the long that Thread.sleep() is expecting? Sorry... a little lost in boxing versus masking Java natives and how BDT fits in.

#13 Updated by Roger Borrello over 4 years ago

I think that I've achieved some basic understanding... If I mask off the lower 32, I should have an unsigned, and not need to check for negative. See how this looks... As for InterruptedException... other thread.

         public static void sleep(int64 millis)
         {
            long unsigned_value = millis.getValue() & 0xFFFFFFFF;
            try
            {
               Thread.sleep(unsigned_value);
            }
            catch (InterruptedException ie)
            {
               // suppress
            }
         }  

#14 Updated by Roger Borrello over 4 years ago

With respect to sending a signal to the running procedure, I believe that it is a child of the 4GL process, and protected from any willy-nilly signal. I've seen this in the procedure editor becoming un-responsive, as it is probably acting on the signal, and still trying to service the running procedure.

I may need to run a process tree tool on the 4GL, or dare I look at a proctrace?

#15 Updated by Greg Shah over 4 years ago

Run the program from outside of the procedure editor (just a command line with the -p <programname>. The procedure editor introduces many changes in the process which can obscure the reality of this. Customer users don't run in the procedure editor so those behaviors are something to ignore.

or dare I look at a proctrace?

What is "proctrace"? If it is just a tool in Windows or the WIN32 SDK, then it is fine.

#16 Updated by Greg Shah over 4 years ago

If I mask off the lower 32, I should have an unsigned,

OK.

#17 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

Run the program from outside of the procedure editor (just a command line with the -p <programname>. The procedure editor introduces many changes in the process which can obscure the reality of this. Customer users don't run in the procedure editor so those behaviors are something to ignore.

or dare I look at a proctrace?

What is "proctrace"? If it is just a tool in Windows or the WIN32 SDK, then it is fine.

procmon... already rolled down my windows! It's Sysinternal tool... Process Monitor. There are many others, but Process Monitor and Process Explorer are very helpful. https://docs.microsoft.com/en-us/sysinternals/downloads/procmon

#18 Updated by Roger Borrello over 4 years ago

Committed in 4207a-11394.

// TODO: It is not clear that os-sleep can be interrupted. With 4GL the ON STOP
// gets handled, but only after the sleep duration completes.
// This was tested with testcase uast/windows_emulation/kernel32_sleep.p
// Note also that on Windows, Control+Break is received as a STOP, but
// Control+C is handled differently. The testcase jumps to "Done." and exits.
// Handling of the InterruptedException may still be required, if we can see
// the behavior when signalling the kernel32:sleep directly.

#19 Updated by Roger Borrello over 4 years ago

  • % Done changed from 0 to 100
  • Status changed from New to WIP

#20 Updated by Greg Shah over 4 years ago

  • Status changed from WIP to Test

#21 Updated by Roger Borrello over 4 years ago

  • Assignee set to Roger Borrello

#22 Updated by Roger Borrello over 4 years ago

List of testcases:

uast/windows_emulation/kernel32_sleep.p
uast/windows_emulation/kernel32_sleep.p.ext-hints
uast/windows_emulation/kernel32_sleep_interrupt.p
uast/windows_emulation/kernel32_sleep_interrupt.p.ext-hints

I added a counter to kernel32_sleep.p, but it's very erratic in the 4GL. It sometimes reports negative.

Also, I get "Sleep" not found. (253)" when I run under FWD. Is there supposed to be an annotation for each method in the NativeAPIEmulation class?

#23 Updated by Greg Shah over 4 years ago

Did the conversion occur successfully?

#24 Updated by Roger Borrello over 4 years ago

Greg Shah wrote:

Did the conversion occur successfully?

Yes...

   @LegacySignature(type = Type.PROCEDURE, name = "Sleep", parameters =
   {
      @LegacyParameter(name = "intMilliseconds", type = "INTEGER", mode = "INPUT")
   })
   public void sleep(final integer _intMilliseconds)
   {
      integer intMilliseconds = TypeFactory.initInput(_intMilliseconds);

      internalProcedure(new Block((Body) () ->
      {
         NativeAPIEmulation.windows.kernel32.sleep(intMilliseconds);
      }));
   }

#25 Updated by Roger Borrello over 4 years ago

I've been trying to catch this in the debugger, but since this is my first rodeo debugging client or server runtime, I may be missing something.

I setup Eclipse debug configs for Remote Java Application:
  • Client using port 5000
  • Server using port 2080
    Both have Source setup to include (along with subdirectories):
  • ~/projects/fwd/4335a-dev/src
  • ~/testcases/src
  1. I start the server in ~/testcases/deploy/server with ./server.sh
  2. I start the client in ~/testcases/deploy/client with ./client.sh -d5000
  3. I attach to the client with Eclipse, and I see the threads.
  4. I load up the ~/testcases/src/com/goldencode/testcases/windows_emulation/Kernel32Sleep.java file in Eclipse, set a breakpoint for after the pause().
  5. I hit a key when the app is asking to Press space bar to continue., and I see the Error popup with "Sleep" was not found. (293).

I was expecting to hit my breakpoint, but I must be missing something.

#26 Updated by Constantin Asofiei over 4 years ago

Roger Borrello wrote:

I was expecting to hit my breakpoint, but I must be missing something.

You need to attach the debugger to the server, not the client.

#27 Updated by Roger Borrello over 4 years ago

Constantin Asofiei wrote:

Roger Borrello wrote:

I was expecting to hit my breakpoint, but I must be missing something.

You need to attach the debugger to the server, not the client.

OK... that's what I did first, in an attempt to hit a breakpoint in ControlFlowOps.java, and I messed up from there.

I hit a breakpoint in Kernel32Sleep.lambda$execute$1 so I'll try to find my way from here.

#28 Updated by Constantin Asofiei over 4 years ago

Roger Borrello wrote:

I hit a breakpoint in Kernel32Sleep.lambda$execute$1 so I'll try to find my way from here.

To avoid all the ControlFlowOps calls, place a breakpoint to Block.body:604 - this will get you right to the actual code for the internal procedure/function.

#29 Updated by Roger Borrello over 4 years ago

Constantin Asofiei wrote:

Roger Borrello wrote:

I hit a breakpoint in Kernel32Sleep.lambda$execute$1 so I'll try to find my way from here.

To avoid all the ControlFlowOps calls, place a breakpoint to Block.body:604 - this will get you right to the actual code for the internal procedure/function.

Thank you!! Probably a worthwhile addition to https://proj.goldencode.com/projects/p2j/wiki/Debugging_Techniques

#30 Updated by Roger Borrello over 4 years ago

When getAbsoluteName(Object referent) is called, jname="com.goldencode.testcases.windows_emulation.Kernel32Sleep". The call to SourceNameMapper.convertJavaProg(jname) comes back null. I'll have to debug into that. Perhaps the Sleep function has not been added to the map?

#31 Updated by Constantin Asofiei over 4 years ago

Roger Borrello wrote:

When getAbsoluteName(Object referent) is called, jname="com.goldencode.testcases.windows_emulation.Kernel32Sleep". The call to SourceNameMapper.convertJavaProg(jname) comes back null. I'll have to debug into that. Perhaps the Sleep function has not been added to the map?

See name_map.xml, if you have the Sleep internal procedure.

Hynek: do you see anything wrong in Roger's approach to convert KERNEL32:SLEEP()?

#32 Updated by Roger Borrello over 4 years ago

./src/com/goldencode/testcases/name_map.xml looks good... although perhaps the pathing is misconfigured?

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<name-map>
  <class-mapping jname="windows_emulation.Kernel32Sleep" pname="windows_emulation/kernel32_sleep.p">
    <method-mapping jname="sleep" pname="Sleep" type="PROCEDURE">
      <parameter jname="intMilliseconds" mode="INPUT" pname="intMilliseconds" type="INTEGER"/>
    </method-mapping>
  </class-mapping>
</name-map>

#33 Updated by Roger Borrello over 4 years ago

I made sure my pathing was correct by validating that directory.xml contains:

              <node class="string" name="pkgroot">
                <node-attribute name="value" value="com.goldencode.testcases"/>
              </node>

So when convertJavaProg() calls removePkg(), the value windows_emulation.Kernel32Sleep is passed to the j2pMap.get(). I would think that's would be matched against jname in name_map.xml, but I may be wrong.

I may debug another testcase, to make sure I implemented Sleep correctly.

#34 Updated by Roger Borrello over 4 years ago

I made a simplified version of uast/myconn1.p:

/* WIN32 API definition */
procedure GetCurrentProcessId external "kernel32":
  define return parameter PID  as long.
end procedure.

def var pid as integer.

run GetCurrentProcessId(output pid).

message "My PID is: " + string(pid).

But is throws an error in the same manner: "GetCurrentProcessId" was not found. (293).

#35 Updated by Roger Borrello over 4 years ago

Eric, did you ever test the myconn1.p testcase under FWD and Windows? I'm trying to get my Kernel32:Sleep function tested, and am not having much luck. That testcase is currently written to handle Win32 using the Kernel32 NativeAPIEmulation, and was for listing DB connections, so you may have see this same issue.

#36 Updated by Eric Faulhaber over 4 years ago

Sorry, no. If this test case is one of mine, I don't remember it.

Are you getting a particular error, or just no result?

#37 Updated by Roger Borrello over 4 years ago

Eric Faulhaber wrote:

Sorry, no. If this test case is one of mine, I don't remember it.

Are you getting a particular error, or just no result?

The NativeAPIEmulation function I added (sleep()) is not found when invoked. I was searching for another testcase that might have helped me understand where I messed up, and found myconn1.p, which you checked it, since it was using Kernel32.dll, but it was a red-herring.

#38 Updated by Constantin Asofiei over 4 years ago

Roger, sorry, I can't make anything to work (gethostbyname, GetComputerNameA). I don't recall how this was supposed to be converted.

Hynek: can you help with NativeApiEmulation ?

#39 Updated by Constantin Asofiei over 4 years ago

Roger, I was missing the .ext-hints. After this, your sleep testcase in #4182-6 works fine.

Please check your classpath - maybe you have something else which overrides the name_map.xml.

#40 Updated by Roger Borrello over 4 years ago

Constantin Asofiei wrote:

Roger, I was missing the .ext-hints. After this, your sleep testcase in #4182-6 works fine.

Please check your classpath - maybe you have something else which overrides the name_map.xml.

Thanks, Constantin. I appreciate it, and am moving on! :-)

#41 Updated by Hynek Cihlar over 4 years ago

Roger Borrello wrote:

Constantin Asofiei wrote:

Roger, I was missing the .ext-hints. After this, your sleep testcase in #4182-6 works fine.

Please check your classpath - maybe you have something else which overrides the name_map.xml.

Thanks, Constantin. I appreciate it, and am moving on! :-)

Sorry guys I couldn't help, but I'm currently on a partial vacation :-).

Greg already suggested to enable the emulation by default, without the need for an ext-hints file. This will prevent this kind of issue in the future.

#42 Updated by Greg Shah over 3 years ago

  • Status changed from Test to Closed

Also available in: Atom PDF