/* * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/ #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h>
#include"sysShmem.h" #include"shmemBase.h" #include"jdwpTransport.h"/* for Packet, TransportCallback */
#ifdefined(_WIN32) #define PRId64 "I64d" #endif
#define MIN(x,y) ((x)<(y)?(x):(y))
/* * This is the base shared memory transport implementation that is used * by both front-end transports (through com.sun.tools.jdi) and * back-end transports (through JDWP_OnLoad and the function tables * it requires). It supports multiple connections for the benefit of the * front-end client; the back end interface assumes only a single connection.
*/
#define MAX_IPC_PREFIX 50 /* user-specified or generated name for */ /* shared memory seg and prefix for other IPC */ #define MAX_IPC_SUFFIX 25 /* suffix to shmem name for other IPC names */ #define MAX_IPC_NAME (MAX_IPC_PREFIX + MAX_IPC_SUFFIX)
#define CHECK_ERROR(expr) do { \
jint error = (expr); \ if (error != SYS_OK) { \
setLastError(error); \ return error; \
} \
} while (0)
#define ENTER_CONNECTION(connection) \ do { \
InterlockedIncrement(&connection->refcount); \ if (IS_STATE_CLOSED(connection->state)) { \
setLastErrorMsg("stream closed"); \
InterlockedDecrement(&connection->refcount); \ return SYS_ERR; \
} \
} while (0)
#define LEAVE_CONNECTION(connection) \ do { \
InterlockedDecrement(&connection->refcount); \
} while (0)
/* * The following assertions should hold anytime the stream's mutex is not held
*/ #define STREAM_INVARIANT(stream) \ do { \
SHMEM_ASSERT((stream->shared->readOffset < SHARED_BUFFER_SIZE) \
&& (stream->shared->readOffset >= 0)); \
SHMEM_ASSERT((stream->shared->writeOffset < SHARED_BUFFER_SIZE) \
&& (stream->shared->writeOffset >= 0)); \
} while (0)
/* * Transports are duplex, so carve the shared memory into "streams", * one used to send from client to server, the other vice versa.
*/ typedefstruct SharedMemoryListener { char mutexName[MAX_IPC_NAME]; char acceptEventName[MAX_IPC_NAME]; char attachEventName[MAX_IPC_NAME];
jboolean isListening;
jboolean isAccepted;
jlong acceptingPID;
jlong attachingPID;
} SharedListener;
/* * Access must be synchronized. Holds one shared * memory buffer and its state.
*/ typedefstruct SharedStream { char mutexName[MAX_IPC_NAME]; char hasDataEventName[MAX_IPC_NAME]; char hasSpaceEventName[MAX_IPC_NAME]; int readOffset; int writeOffset;
jboolean isFull;
jbyte buffer[SHARED_BUFFER_SIZE];
} SharedStream;
/* * The two shared streams: client to server and * server to client.
*/ typedefstruct SharedMemory {
SharedStream toClient;
SharedStream toServer;
} SharedMemory;
/* * Local (to process) access to the shared memory * stream. access to hasData and hasSpace synchronized * by OS.
*/ typedefstruct Stream {
sys_ipmutex_t mutex;
sys_event_t hasData;
sys_event_t hasSpace;
SharedStream *shared;
jint state;
} Stream;
/* * Values for Stream.state field above.
*/ #define STATE_CLOSED 0xDEAD #define STATE_OPEN (STATE_CLOSED -1) /* * State checking macro. We compare against the STATE_OPEN value so * that STATE_CLOSED and any other value will be considered closed. * This catches a freed Stream as long as the memory page is still * valid. If the memory page is gone, then there is little that we * can do.
*/ #define IS_STATE_CLOSED(state) (state != STATE_OPEN)
/* * Set the per-thread error message to the textual representation * of the last system error (if not already set)
*/ staticvoid
setLastError(jint error) { char buf[128];
switch (error) { case SYS_OK : return; /* no-op */ case SYS_DIED : strcpy(buf, "Other process terminated"); break; case SYS_TIMEOUT : strcpy(buf, "Timed out"); break; default : sysGetLastError(buf, sizeof(buf));
}
setLastErrorMsg(buf);
}
/* * Creates named or unnamed event that is automatically reset * (in other words, no need to reset event after it has signalled * a thread).
*/ static jint
createEvent(char *name, void *arg)
{
sys_event_t *retArg = arg; return sysEventCreate(name, retArg, JNI_FALSE);
}
/* enter the stream's mutex and (optionally) check for a closed stream */ static jint
enterMutex(Stream *stream, sys_event_t event)
{
jint ret = sysIPMutexEnter(stream->mutex, event); if (ret != SYS_OK) { if (IS_STATE_CLOSED(stream->state)) {
setLastErrorMsg("stream closed");
} return ret;
} if (IS_STATE_CLOSED(stream->state)) {
setLastErrorMsg("stream closed");
(void)leaveMutex(stream); return SYS_ERR;
} return SYS_OK;
}
/* * Enter/exit with stream mutex held. * On error, does not hold the stream mutex.
*/ static jint
waitForSpace(SharedMemoryConnection *connection, Stream *stream)
{
jint error = SYS_OK;
/* Assumes mutex is held on call */ while ((error == SYS_OK) && FULL(stream)) {
CHECK_ERROR(leaveMutex(stream));
error = sysEventWait(connection->otherProcess, stream->hasSpace, 0); if (error == SYS_OK) {
CHECK_ERROR(enterMutex(stream, connection->shutdown));
} else {
setLastError(error);
}
} return error;
}
/* * Enter/exit with stream mutex held. * On error, does not hold the stream mutex.
*/ static jint
waitForData(SharedMemoryConnection *connection, Stream *stream)
{
jint error = SYS_OK;
/* Assumes mutex is held on call */ while ((error == SYS_OK) && EMPTY(stream)) {
CHECK_ERROR(leaveMutex(stream));
error = sysEventWait(connection->otherProcess, stream->hasData, 0); if (error == SYS_OK) {
CHECK_ERROR(enterMutex(stream, connection->shutdown));
} else {
setLastError(error);
}
} return error;
}
static jint
closeStream(Stream *stream, jboolean linger, volatile DWORD32 *refcount)
{ /* * Lock stream during close - ignore shutdown event as we are * closing down and shutdown should be signalled.
*/
CHECK_ERROR(enterMutex(stream, NULL));
/* mark the stream as closed */
stream->state = STATE_CLOSED; /* wake up waitForData() if it is in sysEventWait() */
sysEventSignal(stream->hasData); /* wake up waitForSpace() if it is in sysEventWait() */
sysEventSignal(stream->hasSpace);
/* * If linger requested then give the stream a few seconds to * drain before closing it.
*/ if (linger) { int attempts = 10; while (!EMPTY(stream) && attempts>0) {
CHECK_ERROR(leaveMutex(stream));
sysSleep(200);
CHECK_ERROR(enterMutex(stream, NULL));
attempts--;
}
}
CHECK_ERROR(leaveMutex(stream));
/* Attempt to close resources */ int attempts = 10; while (attempts > 0) { if (*refcount == 0) {
sysEventClose(stream->hasData);
sysEventClose(stream->hasSpace);
sysIPMutexClose(stream->mutex); return SYS_OK;
}
sysSleep(200);
attempts--;
} return SYS_ERR;
}
/* This process is the client */
connection->incoming.shared = &connection->shared->toClient;
connection->outgoing.shared = &connection->shared->toServer;
/* * Create an event that signals that the connection is shutting * down. The event is unnamed as it's process local, and is * manually reset (so that signalling the event will signal * all threads waiting on it).
*/
error = sysEventCreate(NULL, &connection->shutdown, JNI_TRUE); if (error != SYS_OK) {
setLastError(error);
closeConnection(connection);
freeConnection(connection); return error;
}
*connectionPtr = connection; return SYS_OK;
}
/* * For server: create the shared memory. Create incoming and * outgoing streams.
*/ static jint
createConnection(SharedMemoryTransport *transport, jlong otherPID,
SharedMemoryConnection **connectionPtr)
{
jint error; char streamName[MAX_IPC_NAME];
/* This process is the server */
connection->incoming.shared = &connection->shared->toServer;
connection->outgoing.shared = &connection->shared->toClient;
/* * Create an event that signals that the connection is shutting * down. The event is unnamed as it's process local, and is * manually reset (so that a signalling the event will signal * all threads waiting on it).
*/
error = sysEventCreate(NULL, &connection->shutdown, JNI_TRUE); if (error != SYS_OK) {
setLastError(error);
closeConnection(connection);
freeConnection(connection); return error;
}
error = createConnection(transport, transport->shared->attachingPID,
&connection); if (error != SYS_OK) { /* * Reject the attacher
*/
transport->shared->isAccepted = JNI_FALSE;
sysEventSignal(transport->acceptEvent);
return error;
}
transport->shared->isAccepted = JNI_TRUE;
error = sysEventSignal(transport->acceptEvent); if (error != SYS_OK) { /* * No real point trying to reject it.
*/
closeConnection(connection);
freeConnection(connection); return error;
}
/* lock transport - no additional event to wait on as no connection yet */
error = sysIPMutexEnter(transport->mutex, NULL); if (error != SYS_OK) {
setLastError(error);
closeTransport(transport); return error;
}
if (transport->shared->isListening) {
error = doAttach(transport, timeout); if (error == SYS_OK) {
acceptingPID = transport->shared->acceptingPID;
}
} else { /* Not listening: error */
error = SYS_ERR;
}
sysIPMutexExit(transport->mutex); if (error != SYS_OK) {
closeTransport(transport); return error;
}
void
shmemBase_closeConnection(SharedMemoryConnection *connection)
{
clearLastError();
closeConnection(connection); /* * Ideally we should free the connection structure. However, * since the connection has already being published, other * threads may still be accessing it. In particular, refcount * and state fields could be accessed at any time even after * closing the connection. On Win32 this means we leak 140 * bytes. This memory will be reclaimed at process exit. * * In general reference counting should exist externally to * the object being managed so that it can be freed. If we * want to free SharedMemoryConnection, one alternative could * be to define a new struct X and move all those fields there * except refcount and state. We would have a pointer to a * dynamically allocated X from SharedMemoryConnection. Then * if refcount is 0 we could also free X. This would leak * 12 bytes instead of 140. * * freeConnection(connection); *
*/
}
/* * Read packet header and insert into packet structure. * Allocate space for the data and fill it in.
*/ static jint
shmemBase_receivePacket_internal(SharedMemoryConnection *connection, jdwpPacket *packet)
{
jint data_length;
jint error;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.