Recent

Author Topic: Problems Using timerfd (C Functions)  (Read 4512 times)

guest60499

  • Guest
Problems Using timerfd (C Functions)
« on: July 22, 2018, 05:55:19 am »
Definitions:
Code: Pascal  [Select][+][-]
  1. unit System.Time.Timer.TimerFDTimer;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, CTypes;
  8.  
  9. type
  10.   TFileDescriptor = cint;
  11.  
  12.   // From linux/time.h.
  13.   TClockID = (
  14.     CLOCK_RealTime = 0,
  15.     CLOCK_Monotonic = 1,
  16.     CLOCK_BootTime = 7,
  17.     CLOCK_RealTime_Alarm = 8,
  18.     CLOCK_BootTime_Alarm = 9
  19.   );
  20.  
  21.   csize = LongInt;
  22.   cssize = LongWord;
  23.   ctime = LongWord;
  24.  
  25.   PTimeSpecification = ^TTimeSpecification;
  26.   {$PackRecords C}
  27.   TTimeSpecification = record
  28.     tv_sec: ctime;
  29.     tv_nsec: LongInt;
  30.   end;
  31.  
  32.   PIntervalTimerSpecification = ^TIntervalTimerSpecification;
  33.   {$PackRecords C}
  34.   TIntervalTimerSpecification = record
  35.     it_interval, it_value: TTimeSpecification;
  36.   end;
  37.  
  38.   TTimerFDTimer = class
  39.   private
  40.   public
  41.   end;
  42.  
  43. function timerfd_create(ClockID: TClockID; Flags: cint): TFileDescriptor;
  44. cdecl; external;
  45.  
  46. function timerfd_settime(
  47.   TimerFD: TFileDescriptor;
  48.   Flags: cint;
  49.   const new_value: PIntervalTimerSpecification;
  50.   old_value: PIntervalTimerSpecification): cint;
  51. cdecl; external;
  52.  
  53. function timerfd_gettime(
  54.   TimerFD: TFileDescriptor;
  55.   curr_value: PIntervalTimerSpecification): cint;
  56. cdecl; external;
  57.  
  58. // This is actually just read(2).
  59. function timerfd_read(
  60.   TimerFD: TFileDescriptor;
  61.   Buffer: Pointer;
  62.   Count: csize): cssize;
  63. cdecl; external 'c' name 'read';
  64.  
  65. function clock_gettime(
  66.   clk_id: TClockID;
  67.   tp: PTimeSpecification): cint;
  68. cdecl; external;
  69.  
  70. implementation
  71.  
  72. end.

Main program:
Code: Pascal  [Select][+][-]
  1. program TimerFDTest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Interfaces,
  10.   Forms,
  11.   System.Time.Timer.TimerFDTimer;
  12.  
  13. {$R *.res}
  14.  
  15. var
  16.   TimerFD: TFileDescriptor;
  17.   InterSpec, OldSpec: TIntervalTimerSpecification;
  18.   Now: TTimeSpecification;
  19.   NElapsed: LongInt;
  20.  
  21.   Timer: TTimerFDTimer;
  22.   Tests: TTimerTests;
  23.  
  24. begin
  25.   TimerFD := timerfd_create(CLOCK_RealTime, 0);
  26.   WriteLn(TimerFD);
  27.  
  28.   clock_gettime(CLOCK_Monotonic, @Now);
  29.   WriteLn('Now.tv_sec := ', Now.tv_sec);
  30.   WriteLn('Now.tv_nsec := ', Now.tv_nsec);
  31.  
  32.   InterSpec.it_interval.tv_sec := 10;
  33.   InterSpec.it_interval.tv_nsec := 250000000;
  34.  
  35.   InterSpec.it_value.tv_sec := Now.tv_sec + 1;
  36.   InterSpec.it_value.tv_nsec := 0;
  37.  
  38.   timerfd_settime(TimerFD, 0, @InterSpec, @OldSpec);
  39.  
  40.   WriteLn('OldSpec.it_interval.tv_nsec := ', OldSpec.it_interval.tv_nsec);
  41.   timerfd_gettime(TimerFD, @OldSpec);
  42.   WriteLn('OldSpec.it_interval.tv_nsec := ', OldSpec.it_interval.tv_nsec);
  43.  
  44.   timerfd_read(TimerFD, @NElapsed, SizeOf(NElapsed));
  45.   WriteLn('NElapsed := ', NElapsed);
  46. end.
  47.  

Output:
Code: [Select]
8
Now.tv_sec := 23608
Now.tv_nsec := 0
OldSpec.it_interval.tv_nsec := 0
OldSpec.it_interval.tv_nsec := 2
NElapsed := 0

timerfd_create seems to work. Calling it more than one time increments the resulting file descriptor which is expected. However there is no delay before the program exits as there should be.

I will check it again with C in a bit though I have done this before. Please check for issues with my translation of the structures and function declarations, as well as anything else I may have missed. Seeing as timerfd_gettime returns a strange value I see this as likely.

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Problems Using timerfd (C Functions)
« Reply #1 on: July 22, 2018, 09:01:21 am »
At least one bug:
Code: Pascal  [Select][+][-]
  1. function timerfd_settime(
  2.   TimerFD: TFileDescriptor;
  3.   Flags: cint;
  4.   const new_value: TIntervalTimerSpecification;  // NOT PintervaTimerSpecification (because of const)l
  5.   var old_value: TIntervalTimerSpecification): cint;
  6. cdecl; external;

I am experimenting.
« Last Edit: July 22, 2018, 09:25:52 am by Thaddy »
Specialize a type, not a var.

guest60499

  • Guest
Re: Problems Using timerfd (C Functions)
« Reply #2 on: July 22, 2018, 10:12:42 am »
Per https://www.freepascal.org/docs-html/ref/refsu67.html one should not assume that const parameters are passed any specific way. I did change the pass-by-reference arguments to var, originally I just wanted to mimic the C interface as closely as possible. I changed the const parameter to constref.

It seems timerfd_settime is failing. I've imported BaseUnix but it seems like errno is not being read properly, it always reads as zero.

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Problems Using timerfd (C Functions)
« Reply #3 on: July 22, 2018, 11:40:40 am »
I have a working example:
Code: Pascal  [Select][+][-]
  1. program testtimerfd;
  2. {$ifdef fpc}{$mode delphi}{$H+}{$endif}
  3. {$packrecords C}
  4. uses cthreads, baseunix, sysutils;
  5. const
  6.   _TIMEOUT = 3;
  7.   POLL_PERIOD = 1;
  8.  
  9. type
  10.   TClockID = (
  11.     CLOCK_RealTime = 0,
  12.     CLOCK_Monotonic = 1,
  13.     CLOCK_BootTime = 7,
  14.     CLOCK_RealTime_Alarm = 8,
  15.     CLOCK_BootTime_Alarm = 9
  16.   );
  17.  
  18.   ETimerFDException = class(Exception)
  19.   end;
  20.  
  21.   PTimeSpecification = ^TTimeSpecification;
  22.   TTimeSpecification = record
  23.     tv_sec: dword;
  24.     tv_nsec: LongInt;
  25.   end;
  26.  
  27.    PIntervalTimerSpecification =^TIntervalTimerSpecification;  
  28.    TIntervalTimerSpecification = record
  29.     it_interval, it_value: TTimeSpecification;
  30.   end;
  31.  
  32. function timerfd_create(ClockID: TClockID; Flags: longint): longint;cdecl; external;
  33.  
  34. function timerfd_settime(
  35.   TimerFD: dword;
  36.   Flags: cint;
  37.   new_value: PIntervalTimerSpecification;
  38.   old_value: PIntervalTimerSpecification): cint;cdecl; external;
  39.  
  40. var
  41.   ret,fd:longint;
  42.   timeout:TIntervalTimerSpecification;
  43.   missed:qword;
  44.  
  45. begin
  46.   try
  47.     (* create new timer *)
  48.     fd := timerfd_create(CLOCK_MONOTONIC, 0);
  49.     if fd <= 0 then
  50.       Raise ETimerfdException.Create('Failed to create timer');
  51.        
  52.     (* set to non-blocking *)
  53.     ret := fpfcntl(fd, F_SETFL, O_NONBLOCK);
  54.     if ret <> 0 then
  55.       Raise ETimerfdException.Create('Failed to set to non blocking mode');
  56.        
  57.     (* set timeout *)
  58.     timeout.it_value.tv_sec := _TIMEOUT;
  59.     timeout.it_value.tv_nsec := 0;
  60.     timeout.it_interval.tv_sec := _TIMEOUT; (* recurring *)
  61.     timeout.it_interval.tv_nsec := 0;
  62.     ret := timerfd_settime(fd, 0, @timeout,nil);
  63.     if ret <> 0 then  
  64.       Raise ETimerfdException.Create('Failed to set timer duration');
  65.            
  66.     writeln('Polling');
  67.     while fpread(fd, @missed, sizeof(missed)) < 0 do
  68.     begin
  69.       writeln('No timer expiry');
  70.       sleep(POLL_PERIOD);
  71.     end;
  72.    
  73.     writeln('Number of expiries missed:',missed);
  74.   except
  75.     On E:ETimerfdException do writeln(E.Message) else raise;
  76.   end;
  77. end.
« Last Edit: July 22, 2018, 05:29:16 pm by Thaddy »
Specialize a type, not a var.

guest60499

  • Guest
Re: Problems Using timerfd (C Functions)
« Reply #4 on: July 23, 2018, 03:00:14 am »
Thanks, I noticed a few errors due to your post. The first one was that the call to read(2) expects an 8 byte buffer. The second is that I think BaseUnix declares types improperly for x86_64 system libraries. I had to make more types 64 bit (csize and cssize). The third is, after quite a bit of testing, the swapping of it_value and it_interval in the TIntervalTimerSpecification structure. How do I fix that?

It seems they appear in the reverse order they are specified. I can just switch them, but I would like something more explanatory in the code. The packrecords directive doesn't solve the problem.

Akira1364

  • Hero Member
  • *****
  • Posts: 561
Re: Problems Using timerfd (C Functions)
« Reply #5 on: July 23, 2018, 03:12:42 am »
I'm pretty sure that declaring records as packed and/or using the "packrecords" directive does absolutely nothing at all on 64-bit systems, for what it's worth as a general note.

The assembly code generated is always completely identical. There's a lot of things in the docs that only apply to 32-bit... (i.e. "register" is clearly not the default calling convention on any 64-bit platform, because that's obviously impossible.)

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Problems Using timerfd (C Functions)
« Reply #6 on: July 23, 2018, 02:28:04 pm »
I'm pretty sure that declaring records as packed and/or using the "packrecords" directive does absolutely nothing at all on 64-bit systems, for what it's worth as a general note.

The assembly code generated is always completely identical. There's a lot of things in the docs that only apply to 32-bit... (i.e. "register" is clearly not the default calling convention on any 64-bit platform, because that's obviously impossible.)

packed vs. non-packed only has an effect when there are paddings. E.g. take the following:

Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = record
  3.     a: LongInt;
  4.     b: Int64;
  5.   end;
  6.  

On 32-bit this will have the size 12, no matter whether the record is packed or not. On 64-bit without packing the size will be 16 due to 4 Byte padding between a and b. Packed the size will be 12 again.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11352
  • FPC developer.
Re: Problems Using timerfd (C Functions)
« Reply #7 on: July 23, 2018, 02:36:27 pm »
Per https://www.freepascal.org/docs-html/ref/refsu67.html one should not assume that const parameters are passed any specific way. I did change the pass-by-reference arguments to var, originally I just wanted to mimic the C interface as closely as possible. I changed the const parameter to constref.

It seems timerfd_settime is failing. I've imported BaseUnix but it seems like errno is not being read properly, it always reads as zero.

Baseunix virtualizes Errno.  It might not reflect the libc errno on systems that use system calls. (Linux, *BSD but not OS X)

initc.cerrno is the "real" libc errno.

Similar problems might also affect the types. Libc types (3) can differ from kernel (2) types, and the syscall interface uses the kernel types.

The easiest is make small C programs that dump the sizes of all relevant types.
« Last Edit: July 23, 2018, 02:53:58 pm by marcov »

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Problems Using timerfd (C Functions)
« Reply #8 on: July 23, 2018, 03:57:19 pm »
Thanks, I noticed a few errors due to your post. The first one was that the call to read(2) expects an 8 byte buffer. The second is that I think BaseUnix declares types improperly for x86_64 system libraries. I had to make more types 64 bit (csize and cssize). The third is, after quite a bit of testing, the swapping of it_value and it_interval in the TIntervalTimerSpecification structure. How do I fix that?

It seems they appear in the reverse order they are specified. I can just switch them, but I would like something more explanatory in the code. The packrecords directive doesn't solve the problem.
Well, your code did not work at all. My code worked on 32 bit systems (i386, armhf). 8-) And Ubuntu on x86_64 too. I hope it gave you some idea's.
« Last Edit: July 23, 2018, 04:00:55 pm by Thaddy »
Specialize a type, not a var.

guest60499

  • Guest
Re: Problems Using timerfd (C Functions)
« Reply #9 on: July 23, 2018, 09:00:11 pm »
How did you get it to work on x86_64? Your code as posted won't work due to two of the issues I posted. The more problematic is that with 3.0.4 I need to make some type changes but also switch the order that it_interval and it_value appear in the definition of TIntervalTimerSpecification. Is there any way to avoid this?

Packed does nothing, as some posters assumed.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11352
  • FPC developer.
Re: Problems Using timerfd (C Functions)
« Reply #10 on: July 23, 2018, 09:09:53 pm »
Anything with a time_t is dangerous, since afaik Linux in the process of migrating to a 64-bit time_t ?

guest60499

  • Guest
Re: Problems Using timerfd (C Functions)
« Reply #11 on: July 23, 2018, 09:33:19 pm »
Most of the types that are not defined as language keywords are fairly dangerous as they are liable to change frequently between targets. Quite a few of them seem to even be defined by compiler intrinsics, i.e. typedef __time_t time_t. I'm not exactly sure of a good way to track them.

guest60499

  • Guest
Re: Problems Using timerfd (C Functions)
« Reply #12 on: July 24, 2018, 05:55:02 am »
So this is still ongoing. This struct definition:
Code: Pascal  [Select][+][-]
  1. type
  2.   PTimeSpecification = ^TTimeSpecification;
  3.   TTimeSpecification = record
  4.     tv_sec: QWord;
  5.     tv_nsec: LongWord;
  6.   end;
  7.  
  8.   PIntervalTimerSpecification = ^TIntervalTimerSpecification;
  9.   TIntervalTimerSpecification = record
  10.     it_interval: TTimeSpecification;
  11.     it_value: TTimeSpecification;
  12.   end;  

Gives:
Code: Pascal  [Select][+][-]
  1. var
  2.   InterSpec: TIntervalTimerSpecification;
  3. begin
  4.   WriteLn(@InterSpec.it_interval - @InterSpec);
  5.   WriteLn(@InterSpec.it_value - @InterSpec);
  6. end.
Code: [Select]
0
16

However I need to reverse them for the system call to work, contrary to what is in the manpages and in time.h. What could possibly be wrong?

For reference:
Code: Pascal  [Select][+][-]
  1. function timerfd_settime(
  2.   TimerFD: TFileDescriptor;
  3.   Flags: LongInt;
  4.   constref new_value: TIntervalTimerSpecification;
  5.   var old_value: TIntervalTimerSpecification): cint;
  6. cdecl; external;  

Thaddy

  • Hero Member
  • *****
  • Posts: 14169
  • Probably until I exterminate Putin.
Re: Problems Using timerfd (C Functions)
« Reply #13 on: July 24, 2018, 07:50:50 am »
I will look into it some more. Strange issue. My code really works (others can test that). I am using trunk, but that should not matter in this case since there isn't much code that is new or patched for baseunix. Reversing order like you do is strange,as you noted, because all documentation (also on Debian) suggests otherwise.
Specialize a type, not a var.

 

TinyPortal © 2005-2018