Skip to content

Commit

Permalink
Introduce CS_INTERRUPT_CB and corresponding return values: CS_INT_*.
Browse files Browse the repository at this point in the history
Define CS_INTERRUPT_CB's numeric value alongside other CS_*_CBs';
start a new 94xx range to avoid confusion with properties.
(Sybase/SAP ctlib has no such callback.)  Allow specification on
either the connection or the context level as usual, with the help of
a shim installed on demand to minimize interference with tds_select's
optimization for the no-handler case.  For CS_INT_*, copy TDS_INT_*
and confirm their equality at compile time as with CS_NULLTERM et al.

Add a test roughly modeled on dblib's timeout.c unit test, which
(alone) exercises the corresponding db_setinterrupt API.

Signed-off-by: Aaron M. Ucko <[email protected]>
  • Loading branch information
ucko authored and freddy77 committed Jan 8, 2025
1 parent 691e98d commit bd8c1ca
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 1 deletion.
7 changes: 7 additions & 0 deletions include/cspublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ extern "C"
typedef CS_RETCODE(*CS_CSLIBMSG_FUNC) (CS_CONTEXT *, CS_CLIENTMSG *);
typedef CS_RETCODE(*CS_CLIENTMSG_FUNC) (CS_CONTEXT *, CS_CONNECTION *, CS_CLIENTMSG *);
typedef CS_RETCODE(*CS_SERVERMSG_FUNC) (CS_CONTEXT *, CS_CONNECTION *, CS_SERVERMSG *);
typedef CS_RETCODE(*CS_INTERRUPT_FUNC) (CS_CONNECTION *);


#define CS_IODATA TDS_STATIC_CAST(CS_INT, 1600)
Expand Down Expand Up @@ -453,6 +454,7 @@ enum
#define CS_SECSESSION_CB 8
#define CS_SIGNAL_CB 100
#define CS_MESSAGE_CB 9119
#define CS_INTERRUPT_CB 9400

/* string types */
#define CS_NULLTERM -9
Expand Down Expand Up @@ -789,6 +791,11 @@ enum
#define CS_SERVERMSG_TYPE 4701
#define CS_ALLMSG_TYPE 4702

/* CS_INTERRUPT_CB return values */
#define CS_INT_CONTINUE 1
#define CS_INT_CANCEL 2
#define CS_INT_TIMEOUT 3

CS_RETCODE cs_convert(CS_CONTEXT * ctx, CS_DATAFMT * srcfmt, CS_VOID * srcdata, CS_DATAFMT * destfmt, CS_VOID * destdata,
CS_INT * resultlen);
CS_RETCODE cs_ctx_alloc(CS_INT version, CS_CONTEXT ** ctx);
Expand Down
3 changes: 3 additions & 0 deletions include/ctlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ struct _cs_context
CS_CSLIBMSG_FUNC _cslibmsg_cb;
CS_CLIENTMSG_FUNC _clientmsg_cb;
CS_SERVERMSG_FUNC _servermsg_cb;
CS_INTERRUPT_FUNC _interrupt_cb;
/* code changes start here - CS_CONFIG - 01*/
void *userdata;
int userdata_len;
Expand Down Expand Up @@ -190,6 +191,7 @@ struct _cs_connection
TDSSOCKET *tds_socket;
CS_CLIENTMSG_FUNC _clientmsg_cb;
CS_SERVERMSG_FUNC _servermsg_cb;
CS_INTERRUPT_FUNC _interrupt_cb;
void *userdata;
int userdata_len;
CS_LOCALE *locale;
Expand Down Expand Up @@ -380,6 +382,7 @@ typedef union
*/
TDSRET _ct_handle_server_message(const TDSCONTEXT * ctxptr, TDSSOCKET * tdsptr, TDSMESSAGE * msgptr);
int _ct_handle_client_message(const TDSCONTEXT * ctxptr, TDSSOCKET * tdsptr, TDSMESSAGE * msgptr);
int _ct_handle_interrupt(void * ptr);
TDS_SERVER_TYPE _ct_get_server_type(TDSSOCKET *tds, int datatype);
int _ct_bind_data(CS_CONTEXT *ctx, TDSRESULTINFO * resinfo, TDSRESULTINFO *bindinfo, CS_INT offset);
int _ct_get_client_type(const TDSCOLUMN *col, bool describe);
Expand Down
1 change: 1 addition & 0 deletions include/freetds/tds.h
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,7 @@ typedef struct tds_multiple
/* forward declaration */
typedef struct tds_context TDSCONTEXT;
typedef int (*err_handler_t) (const TDSCONTEXT *, TDSSOCKET *, TDSMESSAGE *);
typedef int (*int_handler_t) (void *);

struct tds_context
{
Expand Down
15 changes: 15 additions & 0 deletions src/ctlib/ct.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ ct_callback(CS_CONTEXT * ctx, CS_CONNECTION * con, CS_INT action, CS_INT type, C
case CS_SERVERMSG_CB:
out_func = (CS_VOID *) (con ? con->_servermsg_cb : ctx->_servermsg_cb);
break;
case CS_INTERRUPT_CB:
out_func = (CS_VOID *) (con ? con->_interrupt_cb : ctx->_interrupt_cb);
break;
default:
_ctclient_msg(ctx, con, "ct_callback()", 1, 1, 1, 5, "%d, %s", type, "type");
return CS_FAIL;
Expand Down Expand Up @@ -389,6 +392,18 @@ ct_callback(CS_CONTEXT * ctx, CS_CONNECTION * con, CS_INT action, CS_INT type, C
else
ctx->_servermsg_cb = (CS_SERVERMSG_FUNC) funcptr;
break;
case CS_INTERRUPT_CB:
if (funcptr) {
if (con)
ctx = con->ctx;
/* install shim on demand */
ctx->tds_ctx->int_handler = _ct_handle_interrupt;
}
if (con)
con->_interrupt_cb = (CS_INTERRUPT_FUNC) funcptr;
else
ctx->_interrupt_cb = (CS_INTERRUPT_FUNC) funcptr;
break;
default:
_ctclient_msg(ctx, con, "ct_callback()", 1, 1, 1, 5, "%d, %s", type, "type");
return CS_FAIL;
Expand Down
18 changes: 18 additions & 0 deletions src/ctlib/ctutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ TEST_EQUAL(t12,CS_COMPUTEFMT_RESULT,TDS_COMPUTEFMT_RESULT);
TEST_EQUAL(t13,CS_ROWFMT_RESULT,TDS_ROWFMT_RESULT);
TEST_EQUAL(t14,CS_MSG_RESULT,TDS_MSG_RESULT);
TEST_EQUAL(t15,CS_DESCRIBE_RESULT,TDS_DESCRIBE_RESULT);
TEST_EQUAL(t16,CS_INT_CONTINUE,TDS_INT_CONTINUE);
TEST_EQUAL(t17,CS_INT_CANCEL,TDS_INT_CANCEL);
TEST_EQUAL(t18,CS_INT_TIMEOUT,TDS_INT_TIMEOUT);

#define TEST_ATTRIBUTE(t,sa,fa,sb,fb) \
TDS_COMPILE_CHECK(t,sizeof(((sa*)0)->fa) == sizeof(((sb*)0)->fb) && TDS_OFFSET(sa,fa) == TDS_OFFSET(sb,fb))
Expand Down Expand Up @@ -161,6 +164,21 @@ _ct_handle_client_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAG
return TDS_INT_CANCEL;
}

/*
* interrupt handler, installed on demand to avoid gratuitously interfering
* with tds_select's optimization for the no-handler case */
int
_ct_handle_interrupt(void * ptr)
{
CS_CONNECTION *con = (CS_CONNECTION *) ptr;
if (con->_interrupt_cb)
return (*con->_interrupt_cb)(con);
else if (con->ctx->_interrupt_cb)
return (*con->ctx->_interrupt_cb)(con);
else
return TDS_INT_CONTINUE;
}

/* message handler */
TDSRET
_ct_handle_server_message(const TDSCONTEXT * ctx_tds, TDSSOCKET * tds, TDSMESSAGE * msg)
Expand Down
1 change: 1 addition & 0 deletions src/ctlib/unittests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@
/variant
/errors
/ct_command
/timeout
/libcommon.a
2 changes: 1 addition & 1 deletion src/ctlib/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ foreach(target t0001 t0002 t0003 t0004
blk_out ct_cursor ct_cursors
ct_dynamic blk_in2 data datafmt rpc_fail row_count
all_types long_binary will_convert
variant errors ct_command)
variant errors ct_command timeout)
add_executable(c_${target} EXCLUDE_FROM_ALL ${target}.c)
set_target_properties(c_${target} PROPERTIES OUTPUT_NAME ${target})
if (target STREQUAL "all_types")
Expand Down
2 changes: 2 additions & 0 deletions src/ctlib/unittests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ TESTS = \
variant$(EXEEXT) \
errors$(EXEEXT) \
ct_command$(EXEEXT) \
timeout$(EXEEXT) \
$(NULL)

check_PROGRAMS = $(TESTS)
Expand Down Expand Up @@ -81,6 +82,7 @@ will_convert_SOURCES = will_convert.c
variant_SOURCES = variant.c
errors_SOURCES = errors.c
ct_command_SOURCES = ct_command.c
timeout_SOURCES = timeout.c

noinst_LIBRARIES = libcommon.a
libcommon_a_SOURCES = common.c common.h
Expand Down
107 changes: 107 additions & 0 deletions src/ctlib/unittests/timeout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Purpose: Test handling of timeouts with callbacks
*/

#include "common.h"
#include <freetds/macros.h>
#include <time.h>

static int ntimeouts = 0, ncancels = 0;
static int max_timeouts = 2, timeout_seconds = 2, cancel_timeout = 10;
static time_t start_time;
static const char sql[] =
"select getdate() as 'begintime'\n"
"waitfor delay '00:00:30'\n"
"select getdate() as 'endtime'";

static int
on_interrupt(CS_CONNECTION *con TDS_UNUSED)
{
printf("In on_interrupt, %ld seconds elapsed.\n", (long int) (time(NULL) - start_time));
return CS_INT_CONTINUE;
}

static int
on_client_msg(CS_CONTEXT *ctx, CS_CONNECTION *con, CS_CLIENTMSG *errmsg)
{
if (errmsg->msgnumber == 20003) { /* TDSETIME */
fprintf(stderr, "%d timeout(s) received in %ld seconds; ", ++ntimeouts, (long int) (time(NULL) - start_time));
if (ntimeouts > max_timeouts) {
if (++ncancels > 1) {
fputs("could not time out cleanly;"
" breaking connection.\n", stderr);
ncancels = 0;
return CS_FAIL;
}
fputs("lost patience;"
" cancelling (allowing 10 seconds)\n", stderr);
if (CS_FAIL == ct_con_props(con, CS_SET, CS_TIMEOUT, &cancel_timeout, CS_UNUSED, NULL))
fputs("... but ct_con_props() failed"
" in error handler.\n", stderr);
return CS_FAIL;
}
fputs("continuing to wait.\n", stderr);
return CS_SUCCEED;
}
return clientmsg_cb(ctx, con, errmsg);
}

static void
test(CS_CONNECTION *con, CS_COMMAND *cmd)
{
CS_INT result_type = 0;
CS_RETCODE ret;

ntimeouts = 0;
ncancels = 0;

printf("Using %d-second query timeouts.\n", timeout_seconds);

if (CS_FAIL == ct_con_props(con, CS_SET, CS_TIMEOUT, &timeout_seconds, CS_UNUSED, NULL)) {
fputs("Failed: ct_con_props(..., CS_SET, CS_TIMEOUT, ...).", stderr);
exit(1);
}

/* Send something that will take a while to execute. */
printf("Issuing a query that will take 30 seconds.\n");
if (CS_SUCCEED != ct_command(cmd, CS_LANG_CMD, (void *) sql, sizeof(sql) - 1, CS_UNUSED)) {
fputs("Failed: ct_command.\n", stderr);
exit(1);
}

start_time = time(NULL); /* Track for reporting purposes. */
ntimeouts = 0;
if (CS_FAIL == ct_callback(NULL, con, CS_SET, CS_CLIENTMSG_CB, &on_client_msg)
|| CS_FAIL == ct_callback(NULL, con, CS_SET, CS_INTERRUPT_CB, &on_interrupt)) {
fputs("Failed: ct_callback.\n", stderr);
exit(1);
}

if (CS_SUCCEED != ct_send(cmd)) {
fputs("Failed: ct_send.\n", stderr);
exit(1);
}

ret = ct_results(cmd, &result_type);
if (ret == CS_SUCCEED) {
fprintf(stderr, "Query unexpectedly succeeded, with result type %d.\n", result_type);
} else {
printf("Query failed as expected, with return code %d.\n", ret);
}
}

int
main(int argc, char **argv)
{
CS_CONTEXT *ctx;
CS_CONNECTION *con;
CS_COMMAND *cmd;

if (CS_SUCCEED != try_ctlogin_with_options(argc, argv, &ctx, &con, &cmd, false)) {
fputs("Customary setup failed.\n", stderr);
return 1;
}
test(con, cmd);
try_ctlogout(ctx, con, cmd, false);
return 0;
}

0 comments on commit bd8c1ca

Please sign in to comment.