Skip to content

[WAIT] PHPC-1274: Reset libmongoc client after forking #941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions php_phongo.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ zend_bool phongo_writeconcernerror_init(zval* return_value, bson_t* bson TSRMLS_
#define PHONGO_ZVAL_CLASS_OR_TYPE_NAME(zv) (Z_TYPE(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE(zv)->name) : zend_get_type_by_const(Z_TYPE(zv)))
#define PHONGO_ZVAL_CLASS_OR_TYPE_NAME_P(zvp) PHONGO_ZVAL_CLASS_OR_TYPE_NAME(*(zvp))

#define PHONGO_CLIENT_RESET_IF_CHIlD_PID(client, created_by_pid) \
do { \
if ((created_by_pid) != getpid()) { \
mongoc_client_reset(client); \
} \
} while (0);

#endif /* PHONGO_H */

/*
Expand Down
4 changes: 4 additions & 0 deletions php_phongo_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef struct {
PHONGO_ZEND_OBJECT_PRE
mongoc_cursor_t* cursor;
mongoc_client_t* client;
int created_by_pid;
uint32_t server_id;
bool advanced;
php_phongo_bson_state visitor_data;
Expand All @@ -78,6 +79,7 @@ typedef struct {
typedef struct {
PHONGO_ZEND_OBJECT_PRE
mongoc_client_t* client;
int created_by_pid;
PHONGO_ZEND_OBJECT_POST
} php_phongo_manager_t;

Expand Down Expand Up @@ -108,12 +110,14 @@ typedef struct {
PHONGO_ZEND_OBJECT_PRE
mongoc_client_t* client;
uint32_t server_id;
int created_by_pid;
PHONGO_ZEND_OBJECT_POST
} php_phongo_server_t;

typedef struct {
PHONGO_ZEND_OBJECT_PRE
mongoc_client_session_t* client_session;
int created_by_pid;
PHONGO_ZEND_OBJECT_POST
} php_phongo_session_t;

Expand Down
6 changes: 6 additions & 0 deletions src/MongoDB/Cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ static void php_phongo_cursor_iterator_move_forward(zend_object_iterator* iter T
cursor->advanced = true;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(cursor->client, cursor->created_by_pid);

if (mongoc_cursor_next(cursor->cursor, &doc)) {
php_phongo_bson_to_zval_ex(bson_get_data(doc), doc->len, &cursor->visitor_data);
} else {
Expand Down Expand Up @@ -382,6 +384,8 @@ static void php_phongo_cursor_free_object(phongo_free_object_arg* object TSRMLS_

zend_object_std_dtor(&intern->std TSRMLS_CC);

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);

if (intern->cursor) {
mongoc_cursor_destroy(intern->cursor);
}
Expand Down Expand Up @@ -428,6 +432,8 @@ static phongo_create_object_retval php_phongo_cursor_create_object(zend_class_en
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
object_properties_init(&intern->std, class_type);

intern->created_by_pid = (int) getpid();

#if PHP_VERSION_ID >= 70000
intern->std.handlers = &php_phongo_handler_cursor;

Expand Down
9 changes: 9 additions & 0 deletions src/MongoDB/Manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ static PHP_METHOD(Manager, executeCommand)
goto cleanup;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_RAW, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);

cleanup:
Expand Down Expand Up @@ -394,6 +395,7 @@ static PHP_METHOD(Manager, executeReadCommand)
return;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_READ, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
} /* }}} */

Expand All @@ -420,6 +422,7 @@ static PHP_METHOD(Manager, executeWriteCommand)
return;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_WRITE, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
} /* }}} */

Expand All @@ -446,6 +449,7 @@ static PHP_METHOD(Manager, executeReadWriteCommand)
return;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_READ_WRITE, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
} /* }}} */

Expand Down Expand Up @@ -481,6 +485,7 @@ static PHP_METHOD(Manager, executeQuery)
goto cleanup;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_query(intern->client, namespace, query, options, server_id, return_value, return_value_used TSRMLS_CC);

cleanup:
Expand Down Expand Up @@ -517,6 +522,7 @@ static PHP_METHOD(Manager, executeBulkWrite)
goto cleanup;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_bulk_write(intern->client, namespace, bulk, options, server_id, return_value, return_value_used TSRMLS_CC);

cleanup:
Expand Down Expand Up @@ -690,6 +696,7 @@ static PHP_METHOD(Manager, startSession)
}
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
cs = mongoc_client_start_session(intern->client, cs_opts, &error);

if (cs) {
Expand Down Expand Up @@ -795,6 +802,8 @@ static phongo_create_object_retval php_phongo_manager_create_object(zend_class_e
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
object_properties_init(&intern->std, class_type);

intern->created_by_pid = (int) getpid();

#if PHP_VERSION_ID >= 70000
intern->std.handlers = &php_phongo_handler_manager;

Expand Down
8 changes: 8 additions & 0 deletions src/MongoDB/Server.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ static PHP_METHOD(Server, executeCommand)

options = php_phongo_prep_legacy_option(options, "readPreference", &free_options TSRMLS_CC);

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_RAW, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);

if (free_options) {
Expand All @@ -71,6 +72,7 @@ static PHP_METHOD(Server, executeReadCommand)
return;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_READ, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
} /* }}} */

Expand All @@ -91,6 +93,7 @@ static PHP_METHOD(Server, executeWriteCommand)
return;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_WRITE, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
} /* }}} */

Expand All @@ -111,6 +114,7 @@ static PHP_METHOD(Server, executeReadWriteCommand)
return;
}

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_command(intern->client, PHONGO_COMMAND_READ_WRITE, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
} /* }}} */

Expand All @@ -134,6 +138,7 @@ static PHP_METHOD(Server, executeQuery)

options = php_phongo_prep_legacy_option(options, "readPreference", &free_options TSRMLS_CC);

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_query(intern->client, namespace, query, options, intern->server_id, return_value, return_value_used TSRMLS_CC);

if (free_options) {
Expand Down Expand Up @@ -165,6 +170,7 @@ static PHP_METHOD(Server, executeBulkWrite)

options = php_phongo_prep_legacy_option(options, "writeConcern", &free_options TSRMLS_CC);

PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
phongo_execute_bulk_write(intern->client, namespace, bulk, options, intern->server_id, return_value, return_value_used TSRMLS_CC);

if (free_options) {
Expand Down Expand Up @@ -570,6 +576,8 @@ static phongo_create_object_retval php_phongo_server_create_object(zend_class_en
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
object_properties_init(&intern->std, class_type);

intern->created_by_pid = (int) getpid();

#if PHP_VERSION_ID >= 70000
intern->std.handlers = &php_phongo_handler_server;

Expand Down
4 changes: 4 additions & 0 deletions src/MongoDB/Session.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ static void php_phongo_session_free_object(phongo_free_object_arg* object TSRMLS

zend_object_std_dtor(&intern->std TSRMLS_CC);

PHONGO_CLIENT_RESET_IF_CHIlD_PID(mongoc_client_session_get_client(intern->client_session), intern->created_by_pid);

if (intern->client_session) {
mongoc_client_session_destroy(intern->client_session);
}
Expand All @@ -468,6 +470,8 @@ static phongo_create_object_retval php_phongo_session_create_object(zend_class_e
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
object_properties_init(&intern->std, class_type);

intern->created_by_pid = (int) getpid();

#if PHP_VERSION_ID >= 70000
intern->std.handlers = &php_phongo_handler_session;

Expand Down
76 changes: 76 additions & 0 deletions tests/cursor/bug1274-001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
--TEST--
PHPC-1274: Cursor destruct should not kill cursor from parent process
--SKIPIF--
<?php if (!function_exists('pcntl_fork')) { die('skip pcntl_fork() not available'); } ?>
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
<?php skip_if_not_live(); ?>
<?php skip_if_not_clean(); ?>
--FILE--
<?php
require_once __DIR__ . "/../utils/basic.inc";

class CommandLogger implements MongoDB\Driver\Monitoring\CommandSubscriber
{
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
{
$command = $event->getCommand();

if ($event->getCommandName() === 'find') {
printf("find command specifies batchSize: %d\n", $command->batchSize);
}

if ($event->getCommandName() === 'getMore') {
printf("getMore command specifies batchSize: %d\n", $command->batchSize);
}
}

public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
{
}

public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
{
}
}

MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger);

$manager = new MongoDB\Driver\Manager(URI);

$bulk = new MongoDB\Driver\BulkWrite();
$bulk->insert(['x' => 1]);
$bulk->insert(['x' => 2]);
$bulk->insert(['x' => 3]);
$manager->executeBulkWrite(NS, $bulk);

$query = new MongoDB\Driver\Query([], ['batchSize' => 2]);
$cursor = $manager->executeQuery(NS, $query);

$parentPid = getmypid();
$childPid = pcntl_fork();

if ($childPid === 0) {
echo "Child exits\n";
exit;
}

if ($childPid > 0) {
$waitPid = pcntl_waitpid($childPid, $status);

if ($waitPid === $childPid) {
echo "Parent waited for child to exit\n";
}

printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor));
}

?>
===DONE===
<?php exit(0); ?>
--EXPECT--
find command specifies batchSize: 2
Child exits
Parent waited for child to exit
getMore command specifies batchSize: 2
Parent fully iterated cursor for 3 documents
===DONE===
81 changes: 81 additions & 0 deletions tests/cursor/bug1274-002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
--TEST--
PHPC-1274: Child process cannot iterate cursor from parent process
--SKIPIF--
<?php if (!function_exists('pcntl_fork')) { die('skip pcntl_fork() not available'); } ?>
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
<?php skip_if_not_live(); ?>
<?php skip_if_not_clean(); ?>
--FILE--
<?php
require_once __DIR__ . "/../utils/basic.inc";

class CommandLogger implements MongoDB\Driver\Monitoring\CommandSubscriber
{
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
{
$command = $event->getCommand();

if ($event->getCommandName() === 'find') {
printf("find command specifies batchSize: %d\n", $command->batchSize);
}

if ($event->getCommandName() === 'getMore') {
printf("getMore command specifies batchSize: %d\n", $command->batchSize);
}
}

public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
{
}

public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
{
}
}

MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger);

$manager = new MongoDB\Driver\Manager(URI);

$bulk = new MongoDB\Driver\BulkWrite();
$bulk->insert(['x' => 1]);
$bulk->insert(['x' => 2]);
$bulk->insert(['x' => 3]);
$manager->executeBulkWrite(NS, $bulk);

$query = new MongoDB\Driver\Query([], ['batchSize' => 2]);
$cursor = $manager->executeQuery(NS, $query);

$parentPid = getmypid();
$childPid = pcntl_fork();

if ($childPid === 0) {
echo throws(function() use ($cursor) {
printf("Child fully iterated cursor for %d documents\n", iterator_count($cursor));
}, 'MongoDB\Driver\Exception\RuntimeException'), "\n";
echo "Child exits\n";
exit;
}

if ($childPid > 0) {
$waitPid = pcntl_waitpid($childPid, $status);

if ($waitPid === $childPid) {
echo "Parent waited for child to exit\n";
}

printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor));
}

?>
===DONE===
<?php exit(0); ?>
--EXPECT--
find command specifies batchSize: 2
OK: Got MongoDB\Driver\Exception\RuntimeException
Cannot advance cursor after client reset
Child exits
Parent waited for child to exit
getMore command specifies batchSize: 2
Parent fully iterated cursor for 3 documents
===DONE===
Loading