rpc_lesson3

Two models run in a client-server, remote procedure call (RPC) communication pattern. The client model client sends requests and receives response to/from the server model server via the deprecated call method. The server model receives requests and send responses. The client model receives request data from a file and send responses to a file. In the case of both models, yggdrasil auto-wraps functions with interface calls at runtime. This example demostrates the RPC pattern through the use of the client_of and is_server model parameters and the RPC interface classes/functions in conjunction with automated wrapping of model functions, including the use of the global_scope interface parameter when autowrapping functions that contain interface calls themselves.

C Version

Model Code:

 1#include <stdio.h>
 2
 3int model_function(char *in_buf, uint64_t length_in_buf,
 4		   char **out_buf, uint64_t *length_out_buf) {
 5  printf("server(C): %s\n", in_buf);
 6  length_out_buf[0] = length_in_buf;
 7  out_buf[0] = (char*)malloc(length_in_buf);
 8  memcpy(out_buf[0], in_buf, length_in_buf);
 9  out_buf[0][length_in_buf] = '\0';
10  return 0;
11};
 1#include "YggInterface.h"
 2#include <stdio.h>
 3
 4
 5int model_function(char* in_buf, uint64_t length_in_buf,
 6		   char** out_buf, uint64_t* length_out_buf) {
 7  // The WITH_GLOBAL_SCOPE macro is required to ensure that the comm persists
 8  // between function calls
 9  WITH_GLOBAL_SCOPE(yggRpc_t rpc = yggRpcClient("server_client", "%s", "%s"));
10  printf("client(C): %s (length = %d)\n", in_buf, (int)(length_in_buf));
11  int ret = rpcCallRealloc(rpc, in_buf, length_in_buf, out_buf, length_out_buf);
12  if (ret < 0) {
13    printf("client(C): RPC CALL ERROR\n");
14    return -1;
15  }
16  return 0;
17}

Model YAML:

 1---
 2
 3model:
 4  name: server
 5  language: c
 6  args: ./src/server.c
 7  function: model_function
 8  is_server:  # Creates a RPC server queue called "server"
 9    input: in_buf
10    output: out_buf
11  inputs: in_buf
12  outputs: out_buf
 1model:
 2  name: client
 3  language: c
 4  args: ./src/client.c
 5  function: model_function
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true

C++ Version

Model Code:

 1#include <iostream>
 2
 3int model_function(char *in_buf, uint64_t length_in_buf,
 4		   char* &out_buf, uint64_t &length_out_buf) {
 5  std::cout << "server(C++): " << in_buf << std::endl;
 6  length_out_buf = length_in_buf;
 7  out_buf = (char*)realloc(out_buf, length_in_buf);
 8  memcpy(out_buf, in_buf, length_in_buf);
 9  out_buf[length_in_buf] = '\0';
10  return 0;
11};
 1#include "YggInterface.hpp"
 2#include <iostream>
 3
 4
 5int model_function(char *in_buf, uint64_t length_in_buf,
 6		   char* &out_buf, uint64_t &length_out_buf) {
 7  // The WITH_GLOBAL_SCOPE macro is required to ensure that the comm persists
 8  // between function calls
 9  WITH_GLOBAL_SCOPE(YggRpcClient rpc("server_client", "%s", "%s"));
10  std::cout << "client(C++): " << in_buf << " (length = " << length_in_buf << ")" << std::endl;
11  int ret = rpc.callRealloc(4, in_buf, length_in_buf, &out_buf, &length_out_buf);
12  if (ret < 0) {
13    std::cout << "client(C++): RPC CALL ERROR" << std::endl;
14    return -1;
15  }
16  return 0;
17}

Model YAML:

1---
2
3model:
4  name: server
5  language: c++
6  args: ./src/server.cpp
7  function: model_function
8  is_server: True  # Creates a RPC server queue called "server"
 1model:
 2  name: client
 3  language: c++
 4  args: ./src/client.cpp
 5  function: model_function
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true

Fortran Version

Model Code:

1function model_function(in_buf, out_buf) result(out)
2  character(len=*), intent(in) :: in_buf
3  character(len=:), pointer :: out_buf
4  logical :: out
5  write(*, '("server(Fortran): ",A)') in_buf
6  out = .true.
7  allocate(character(len=len(in_buf)) :: out_buf)
8  out_buf = in_buf
9end function model_function
 1function model_function(in_buf, out_buf) result(ret)
 2  character(len=*), intent(in) :: in_buf
 3  type(yggchar_r) :: out_buf
 4  logical :: ret
 5  type(yggcomm) :: rpc
 6  WITH_GLOBAL_SCOPE(rpc = ygg_rpc_client("server_client"))
 7  write(*, '("client(F): ",A," (length = ",I3,")")') in_buf, len(in_buf)
 8  ret = ygg_rpc_call_realloc(rpc, yggarg(in_buf), yggarg(out_buf))
 9  if (.not.ret) then
10     write(*, '("client(F): RPC CALL ERROR")')
11     stop 1
12  end if
13end function model_function

Model YAML:

1---
2
3model:
4  name: server
5  language: fortran
6  args: ./src/server.f90
7  function: model_function
8  is_server: True  # Creates a RPC server queue called "server"
 1model:
 2  name: client
 3  language: fortran
 4  args: ./src/client.f90
 5  function: model_function
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true

Julia Version

Model Code:

1using Yggdrasil
2using Printf
3
4function model_function(in_buf)
5  @printf("server(Julia): %s\n", in_buf)
6  out_buf = in_buf
7  return out_buf
8end
 1using Yggdrasil
 2using Printf
 3
 4function model_function(in_buf)
 5  # The global_scope keyword is required to ensure that the comm persists
 6  # between function calls
 7  rpc = Yggdrasil.YggInterface("YggRpcClient", "server_client", global_scope=true)
 8  @printf("client(Julia): %s\n", in_buf)
 9  ret, result = rpc.call(in_buf)
10  if (!ret)
11    error("client(Julia): RPC CALL ERROR")
12  end
13  out_buf = result
14  return out_buf
15end

Model YAML:

1---
2
3model:
4  name: server
5  language: julia
6  args: ./src/server.jl
7  function: model_function
8  is_server: True  # Creates a RPC server queue called "server"
 1model:
 2  name: client
 3  language: julia
 4  args: ./src/client.jl
 5  function: model_function
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true

Matlab Version

Model Code:

1function out_buf = server(in_buf)
2  fprintf('server(Matlab): %s\n', in_buf);
3  out_buf = in_buf;
4end
 1function out_buf = client(in_buf)
 2  % The global_scope keyword is required to ensure that the comm persists
 3  % between function calls
 4  rpc = YggInterface('YggRpcClient', 'server_client', 'global_scope', true);
 5  disp(sprintf('client(Matlab): %s', in_buf));
 6  [ret, result] = rpc.call(in_buf);
 7  if (~ret);
 8    error('client(Matlab): RPC CALL ERROR');
 9  end;
10  out_buf = result;
11end

Model YAML:

1---
2
3model:
4  name: server
5  language: matlab
6  args: ./src/server.m
7  function: server  # matlab requires function to match file
8  is_server: True  # Creates a RPC server queue called "server"
 1model:
 2  name: client
 3  language: matlab
 4  args: ./src/client.m
 5  function: client  # Matlab requires the same name
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true

Python Version

Model Code:

1def model_function(in_buf):
2    print("server(Python): %s" % in_buf)
3    out_buf = in_buf
4    return out_buf
 1from yggdrasil.languages.Python.YggInterface import YggRpcClient
 2
 3
 4def model_function(in_buf):
 5    # The global_scope keyword is required to ensure that the comm persists
 6    # between function calls
 7    rpc = YggRpcClient('server_client', global_scope=True)
 8    print("client(Python): %s" % in_buf)
 9    ret, result = rpc.call(in_buf)
10    if not ret:
11        raise RuntimeError('client(Python): RPC CALL ERROR')
12    out_buf = result
13    return out_buf

Model YAML:

1model:
2  name: server
3  language: python
4  args: ./src/server.py
5  function: model_function
6  is_server: True
 1model:
 2  name: client
 3  language: python
 4  args: ./src/client.py
 5  function: model_function
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true

R Version

Model Code:

1model_function <- function(in_buf) {
2  fprintf('server(R): %s', in_buf)
3  out_buf <- in_buf
4  return(out_buf);
5}
 1model_function <- function(in_buf) {
 2  # The global_scope keyword is required to ensure that the comm persists
 3  # between function calls
 4  rpc <- YggInterface('YggRpcClient', 'server_client', global_scope=TRUE)
 5  print(sprintf("client(R): %s", in_buf))
 6  c(ret, result) %<-% rpc$call(in_buf)
 7  if (!ret) {
 8    stop('client(R): RPC CALL ERROR')
 9  }
10  out_buf <- result[[1]]
11  return(out_buf)
12}

Model YAML:

1---
2
3model:
4  name: server
5  language: R
6  args: ./src/server.R
7  function: model_function
8  is_server: True  # Creates a RPC server queue called "server"
 1model:
 2  name: client
 3  language: R
 4  args: ./src/client.R
 5  function: model_function
 6  client_of: server
 7  inputs:
 8    name: in_buf
 9    default_file:
10      name: ./Input/input.txt
11      filetype: ascii
12  outputs:
13    name: out_buf
14    default_file:
15      name: ./client_output.txt
16      in_temp: true