1秒より短い間隔でDLYJOBしたい時のプログラムを作ってみました。

## はじめに
CLプログラムで処理と処理の間に待ちを作りたい時はDLYJOBコマンドを使います。
DLYJOBコマンドは待機する秒数ないしは再開の時刻を指定することができてとても便利ですよね。
[DLYJOBコマンド](https://www.ibm.com/docs/ja/i/7.5?topic=ssw_ibm_i_75/cl/dlyjob.html)

ただ、DLYJOBコマンドの最小の待ちインターバルとしては1秒です。
そのため、1秒よりも短いインターバルでの待ちはDLYJOBコマンドでは作れません。
先日1秒よりも短い間隔で待ちを作りたいということがありまして、そのためのプログラムをILE RPGで作ってみました。

## pthread_delay_npサブルーチンを使おう
スレッド・プログラミングのサブルーチンとしてpthread_delay_npがありますが、IBM i でもAPIとして用意されています。
[pthread_delay_np -要求された間隔のスレッドの遅延](https://www.ibm.com/docs/ja/i/7.5?topic=ssw_ibm_i_75/apis/users_15.html)

このAPIの入力パラメーターは上記マニュアルによると、C言語のtimespec構造体のアドレスを渡せば良いようですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1305251/88612c35-d64f-e2e7-6ab2-a024992db195.png)

timespec構造体は以下のようなもののようです。
pthread_delay_npでは、tv_secに待機の整数秒、tv_nsecに小数点以下の秒をナノ秒としてセットする形です。
```
#include <time.h>
struct timespec {
  time_t tv_sec; /* Seconds.  */
  long tv_nsec;  /* Nanoseconds.  */
};
```
ILE RPG (Free Form) では以下のデータ構造として定義しました。
```
dcl-ds timeSpecDs qualified;
  seconds int(10);
  nanoseconds int(10);
end-ds;
```
## 作成プログラム
今回作成したプログラムはこんな感じです。
入力パラメーターとして渡された数値を整数と小数点以下に分けて、データ構造timeSpecDsに入れています。
...ロジックはほぼなしですね（汗
``` DLYJOBNS.RPGLE
      **free
        
        //全体制御
        ctl-opt dftactgrp(*no) main(main) option(*srcstmt) bnddir('QC2LE');

        //メイン・プロシージャー
        dcl-proc main;
          //入出力パラメーター定義
          dcl-pi *n;
            p_seconds packed(8:3);
          end-pi;

          //利用機能プロトタイプ定義
          dcl-pr pthreadDelayNp int(5) extproc('pthread_delay_np');
            *n pointer value;
          end-pr;

          dcl-ds timeSpecDs qualified;
            seconds int(10);
            nanoseconds int(10);
          end-ds;

          //メイン・ロジック
          timeSpecDs.seconds
              = %int(p_seconds);
          timeSpecDs.nanoseconds
              = %int(%dec(%dec(p_seconds) - %dec(%int(p_seconds))) 
              * (10 ** 9));

          pthreadDelayNp(%addr(timeSpecDs));

          return;
        end-proc;
```
今回はソースをIFS上に置いて、以下のCRTBNDRPGコマンドで作成しました。
IFS上のソースはUTF-8 (CCSID 1208) でしたので、TGTCCSIDパラメーターを明示指定しています。
```
CRTBNDRPG PGM(PVSONE/DLYJOBNS) SRCSTMF('/home/NAKAMURA/QRPGLESRC/DLYJOBNS.RPGLE') DFTACTGRP(*NO) DBGVIEW(*SOURCE) TGTCCSID(5035)
```

## プログラムのテスト
DLYJOBNSプログラム実行前後の時刻をミリ秒までの17桁 (YYYYMMDDhhmmsssss) をDSPLY命令で出すILE RPGプログラムを作って実行してみました。
(ちなみに中で使用しているプログラムGETCURTIMEはRTVSYSVAL SYSVAL(QDATETIME) の値をミリ秒までにした値を返すものです。)
``` DLYTEST.RPGLE
      *free

        //全体制御
        ctl-opt dftactgrp(*no) main(main);

        //メイン・プロシージャー
        dcl-proc main;
          //入出力パラメーター定義
          dcl-pi *n;
            p_seconds packed(8:3);
          end-pi;

          //プロトタイプ定義
          dcl-pr GETCURTIME extpgm('GETCURTIME');
            *n char(17);
          end-pr;

          dcl-pr DLYJOBNS extpgm('DLYJOBNS');
            *n packed(8:3);
          end-pr;

          //ローカル変数
          dcl-s time char(17);
          dcl-s msg char(50);

          //メイン・ロジック
          callp GETCURTIME(time);
          msg = 'DELAY START ' + time;
          dsply msg;

          callp DLYJOBNS(p_seconds);

          callp GETCURTIME(time);
          msg = 'DELAY END   ' + time;
          dsply msg;

          return;
        end-proc;
```

時刻取得プログラムとDSPLY命令の実行で若干時間がかかっていますが、一応それっぽい時間だけDLYJOBNSで待ってくれていそうですね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1305251/c5a9dc6d-ac34-6fbb-0f8f-b897afa95a65.png)

ちなみに、前述のpthread_delay_npのマニュアルには
> Although time is specified in seconds and nanoseconds, the system has approximately millisecond granularity. Due to scheduling and priorities, the amount of time you actually wait might be slightly more or less than the amount of time specified.

とありました。ナノ秒の指定ですがミリ秒の粒度ということです。
あくまでおおよそで待ってくれる感じですが、IBM i で非常にちょっとした待ちを作りたい時の参考になればと思います。