time management module
This commit introduces a time management module. See doc/time_management.md for details.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
add_executable(test_time_manager_manual test_manual.c)
|
||||
target_link_libraries(test_time_manager_manual time_management_core)
|
||||
target_link_libraries(test_time_manager_manual LOG CONFIG_LIB)
|
||||
|
||||
add_executable(test_time_manager_auto test_auto.c)
|
||||
target_link_libraries(test_time_manager_auto time_management)
|
||||
target_link_libraries(test_time_manager_auto LOG CONFIG_LIB)
|
||||
|
||||
add_dependencies(tests test_time_manager_auto)
|
||||
add_test(NAME time_management_tests
|
||||
COMMAND ./test_time_manager_auto)
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "common/utils/time_manager/time_manager.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
|
||||
configmodule_interface_t *uniqCfg = NULL;
|
||||
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
||||
{
|
||||
if (s != NULL) {
|
||||
printf("%s:%d %s() Exiting OAI softmodem: %s\n", file, line, function, s);
|
||||
}
|
||||
if (assert)
|
||||
abort();
|
||||
else
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int is_x2ap_enabled(void)
|
||||
{
|
||||
/* we want x2ap ticks */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rlc_tick_count = 0;
|
||||
static int pdcp_tick_count = 0;
|
||||
static int x2ap_tick_count = 0;
|
||||
|
||||
void nr_rlc_ms_tick(void)
|
||||
{
|
||||
rlc_tick_count++;
|
||||
}
|
||||
|
||||
void nr_pdcp_ms_tick(void)
|
||||
{
|
||||
pdcp_tick_count++;
|
||||
}
|
||||
|
||||
void x2ap_ms_tick(void)
|
||||
{
|
||||
x2ap_tick_count++;
|
||||
}
|
||||
|
||||
/* return 1 if ok, 0 if error */
|
||||
static int standalone_realtime(void)
|
||||
{
|
||||
time_manager_start(TIME_MANAGER_GNB_MONOLITHIC, TIME_MANAGER_REALTIME);
|
||||
|
||||
sleep(1);
|
||||
|
||||
time_manager_finish();
|
||||
|
||||
LOG_I(UTIL,
|
||||
"standalone realtime: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n",
|
||||
rlc_tick_count,
|
||||
pdcp_tick_count,
|
||||
x2ap_tick_count);
|
||||
|
||||
/* let's accept 1% deviation from the 1000 counts we expect */
|
||||
return abs(rlc_tick_count-1000) < 10
|
||||
&& abs(pdcp_tick_count - 1000) < 10
|
||||
&& abs(x2ap_tick_count - 1000) < 10;
|
||||
}
|
||||
|
||||
/* return 1 if ok, 0 if error */
|
||||
static int standalone_iq_samples(void)
|
||||
{
|
||||
time_manager_start(TIME_MANAGER_GNB_MONOLITHIC, TIME_MANAGER_IQ_SAMPLES);
|
||||
|
||||
/* let's pretend we have 2000 samples per second */
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
time_manager_iq_samples(1, 2000);
|
||||
usleep(10);
|
||||
time_manager_iq_samples(1, 2000);
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
time_manager_finish();
|
||||
|
||||
LOG_I(UTIL,
|
||||
"standalone iq-samples: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n",
|
||||
rlc_tick_count,
|
||||
pdcp_tick_count,
|
||||
x2ap_tick_count);
|
||||
|
||||
/* exact count is required */
|
||||
return rlc_tick_count == 1000
|
||||
&& pdcp_tick_count == 1000
|
||||
&& x2ap_tick_count == 1000;
|
||||
}
|
||||
|
||||
/* return 1 if ok, 0 if error */
|
||||
static int client_server(char *program_name, bool iq_samples_time_source)
|
||||
{
|
||||
LOG_I(UTIL, "client/server %s: launch sub-processes\n", iq_samples_time_source ? "iq-samples" : "realtime");
|
||||
|
||||
pid_t server;
|
||||
pid_t client;
|
||||
|
||||
/* start server */
|
||||
server = fork();
|
||||
DevAssert(server >= 0);
|
||||
if (server == 0) {
|
||||
/* run as server */
|
||||
if (iq_samples_time_source)
|
||||
execl(program_name, program_name, "--time_management.mode", "server", "--time_management.time_source", "iq_samples", NULL);
|
||||
else
|
||||
execl(program_name, program_name, "--time_management.mode", "server", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* wait a little bit so that server is ready to accept clients
|
||||
* (this is hackish)
|
||||
*/
|
||||
usleep(100);
|
||||
|
||||
/* start client */
|
||||
client = fork();
|
||||
DevAssert(client >= 0);
|
||||
if (client == 0) {
|
||||
/* run as client */
|
||||
execl(program_name, program_name, "--time_management.mode", "client", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* wait for completion */
|
||||
int ret = 1;
|
||||
|
||||
int status;
|
||||
pid_t r = waitpid(server, &status, 0);
|
||||
DevAssert(r == server);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
ret = 0;
|
||||
|
||||
r = waitpid(client, &status, 0);
|
||||
DevAssert(r == client);
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return 1 if ok, 0 if error */
|
||||
static int run_sub_client_server(bool iq_samples_time_source)
|
||||
{
|
||||
/* second argument doesn't matter, overwritten by args of execl in client_server() */
|
||||
time_manager_start(TIME_MANAGER_GNB_MONOLITHIC, TIME_MANAGER_REALTIME);
|
||||
|
||||
if (iq_samples_time_source) {
|
||||
/* 'iq_samples_time_source' is true only for server
|
||||
* wait a bit for client to be there otherwise server
|
||||
* may finish too early
|
||||
*/
|
||||
usleep(100);
|
||||
|
||||
/* let's pretend we have 2000 samples per second */
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
time_manager_iq_samples(1, 2000);
|
||||
usleep(10);
|
||||
time_manager_iq_samples(1, 2000);
|
||||
usleep(10);
|
||||
}
|
||||
} else {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
time_manager_finish();
|
||||
|
||||
LOG_I(UTIL, "client/server: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n", rlc_tick_count, pdcp_tick_count, x2ap_tick_count);
|
||||
|
||||
if (iq_samples_time_source)
|
||||
return rlc_tick_count == 1000
|
||||
&& pdcp_tick_count == 1000
|
||||
&& x2ap_tick_count == 1000;
|
||||
else
|
||||
/* let's accept 1% deviation from the 1000 counts we expect */
|
||||
return abs(rlc_tick_count-1000) < 10
|
||||
&& abs(pdcp_tick_count - 1000) < 10
|
||||
&& abs(x2ap_tick_count - 1000) < 10;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) {
|
||||
exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
|
||||
}
|
||||
logInit();
|
||||
|
||||
/* for client/server tests, we use sub-processes with command line parameters */
|
||||
if (argc > 1) {
|
||||
/* if there are command line parameters then it's a sub-process */
|
||||
rlc_tick_count = 0;
|
||||
pdcp_tick_count = 0;
|
||||
x2ap_tick_count = 0;
|
||||
/* return value to the main test program
|
||||
* let's do as for the shell: 0 means success and 1 means error
|
||||
*/
|
||||
/* we know we are in iq-samples time source mode if the last argument is "iq_samples" */
|
||||
int success = run_sub_client_server(!strcmp(argv[argc - 1], "iq_samples")) == 1;
|
||||
return 1 - success;
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
|
||||
/* test standalone realtime */
|
||||
rlc_tick_count = 0;
|
||||
pdcp_tick_count = 0;
|
||||
x2ap_tick_count = 0;
|
||||
int ret = standalone_realtime();
|
||||
if (ret == 1)
|
||||
LOG_I(UTIL, "standalone realtime: OK\n");
|
||||
else {
|
||||
LOG_E(UTIL, "standalone realtime: ERROR\n");
|
||||
err = 1;
|
||||
}
|
||||
|
||||
/* test standalone iq-samples */
|
||||
rlc_tick_count = 0;
|
||||
pdcp_tick_count = 0;
|
||||
x2ap_tick_count = 0;
|
||||
ret = standalone_iq_samples();
|
||||
if (ret == 1)
|
||||
LOG_I(UTIL, "standalone iq-samples: OK\n");
|
||||
else {
|
||||
LOG_E(UTIL, "standalone iq-samples: ERROR\n");
|
||||
err = 1;
|
||||
}
|
||||
|
||||
/* client/server tests are not written in a robust way
|
||||
* they may fail depending on realtime on the machine
|
||||
* de-activate if needed (if CI fails)
|
||||
*/
|
||||
/* test client/server realtime */
|
||||
rlc_tick_count = 0;
|
||||
pdcp_tick_count = 0;
|
||||
x2ap_tick_count = 0;
|
||||
ret = client_server(argv[0], false);
|
||||
if (ret == 1)
|
||||
LOG_I(UTIL, "client-server realtime: OK\n");
|
||||
else {
|
||||
LOG_E(UTIL, "client-server realtime: ERROR\n");
|
||||
err = 1;
|
||||
}
|
||||
|
||||
/* test client/server iq-samples */
|
||||
rlc_tick_count = 0;
|
||||
pdcp_tick_count = 0;
|
||||
x2ap_tick_count = 0;
|
||||
ret = client_server(argv[0], true);
|
||||
if (ret == 1)
|
||||
LOG_I(UTIL, "client-server iq-samples: OK\n");
|
||||
else {
|
||||
LOG_E(UTIL, "client-server iq-samples: ERROR\n");
|
||||
err = 1;
|
||||
}
|
||||
|
||||
logTerm();
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "../time_source.h"
|
||||
#include "../time_server.h"
|
||||
#include "../time_client.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "common/utils/system.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
#include "common/config/config_userapi.h"
|
||||
|
||||
configmodule_interface_t *uniqCfg = NULL;
|
||||
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
||||
{
|
||||
printf("%s:%d:%s: %s\n", file, line, function, s);
|
||||
fflush(stdout);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_Atomic bool test_exit;
|
||||
|
||||
static void *iq_generate_thread(void *ts)
|
||||
{
|
||||
time_source_t *time_source = ts;
|
||||
|
||||
while (!test_exit) {
|
||||
printf("iq_generate_thread calls time_source_iq_add(time_source, 100, 10000)\n");
|
||||
time_source_iq_add(time_source, 100, 10000);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define STANDALONE 0
|
||||
#define SERVER 1
|
||||
#define CLIENT 2
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("options:\n");
|
||||
printf(" -client\n");
|
||||
printf(" run as client (default is standalone)\n");
|
||||
printf(" -server\n");
|
||||
printf(" run as server (default is standalone)\n");
|
||||
printf(" -ip <ip address (default 127.0.0.1)>\n");
|
||||
printf(" use this ip address for server\n");
|
||||
printf(" -port <port (default 7473)\n");
|
||||
printf(" use this port for server\n");
|
||||
printf(" -realtime\n");
|
||||
printf(" run with realtime clock (default is simulated iq samples)\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void server_callback(void *callback_data)
|
||||
{
|
||||
printf("server_callback called (callback_data %p)\n", callback_data);
|
||||
DevAssert(callback_data == (void *)1);
|
||||
}
|
||||
|
||||
static void client_callback(void *callback_data)
|
||||
{
|
||||
printf("client_callback called (callback_data %p)\n", callback_data);
|
||||
DevAssert(callback_data == (void *)2);
|
||||
}
|
||||
|
||||
/* dummy sig handler to quit immediately when pressing enter
|
||||
* (we send a signal to iq_generate_thread to interrupt sleep())
|
||||
*/
|
||||
static void sig_handle(int n)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
time_source_t *ts = NULL;
|
||||
time_server_t *server = NULL;
|
||||
time_client_t *client = NULL;
|
||||
int mode = STANDALONE;
|
||||
char *server_ip = "127.0.0.1";
|
||||
int server_port = 7473;
|
||||
time_source_type_t time_source_type = TIME_SOURCE_IQ_SAMPLES;
|
||||
pthread_t iq_thread;
|
||||
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-client")) { mode = CLIENT; continue; }
|
||||
if (!strcmp(v[i], "-server")) { mode = SERVER; continue; }
|
||||
if (!strcmp(v[i], "-ip")) { if (i>n-2) usage(); server_ip = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-port")) { if (i>n-2) usage(); server_port = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-realtime")) { time_source_type = TIME_SOURCE_REALTIME; continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
logInit();
|
||||
/* hack: set log to info (logInit doesn't do it because we don't call load_configmodule()) */
|
||||
for (int i = 0; i < MAX_LOG_COMPONENTS; i++)
|
||||
g_log->log_component[i].level = OAILOG_INFO;
|
||||
|
||||
if (mode != CLIENT) {
|
||||
ts = new_time_source(time_source_type);
|
||||
if (time_source_type == TIME_SOURCE_IQ_SAMPLES) {
|
||||
void *ret = signal(SIGHUP, sig_handle);
|
||||
DevAssert(ret != SIG_ERR);
|
||||
threadCreate(&iq_thread, iq_generate_thread, ts, "iq samples generator", -1, SCHED_OAI);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == SERVER) {
|
||||
/* (void*)1 to check if callback data is passed correctly */
|
||||
server = new_time_server(server_ip, server_port, server_callback, (void *)1);
|
||||
time_server_attach_time_source(server, ts);
|
||||
}
|
||||
|
||||
if (mode == CLIENT) {
|
||||
/* (void*)2 to check if callback data is passed correctly */
|
||||
client = new_time_client(server_ip, server_port, client_callback, (void *)2);
|
||||
}
|
||||
|
||||
printf("main: press enter to quit\n");
|
||||
getchar();
|
||||
|
||||
test_exit = true;
|
||||
|
||||
if (mode != CLIENT)
|
||||
if (time_source_type == TIME_SOURCE_IQ_SAMPLES) {
|
||||
(void)pthread_kill(iq_thread, SIGHUP);
|
||||
int ret = pthread_join(iq_thread, NULL);
|
||||
DevAssert(ret == 0);
|
||||
}
|
||||
|
||||
if (mode != CLIENT) {
|
||||
free_time_source(ts);
|
||||
}
|
||||
|
||||
if (mode == SERVER) {
|
||||
free_time_server(server);
|
||||
}
|
||||
|
||||
if (mode == CLIENT) {
|
||||
free_time_client(client);
|
||||
}
|
||||
|
||||
logTerm();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user