text: /*******************************************************************************
   * Copyright (c) 2013, 2016 IBM Corp. and others
   *
   * This program and the accompanying materials are made available under
   * the terms of the Eclipse Public License 2.0 which accompanies this
   * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
   * or the Apache License, Version 2.0 which accompanies this distribution and
   * is available at https://www.apache.org/licenses/LICENSE-2.0.
   *
   * This Source Code may also be made available under the following
   * Secondary Licenses when the conditions for such availability set
   * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
   * General Public License, version 2 with the GNU Classpath
   * Exception [1] and GNU General Public License, version 2 with the
   * OpenJDK Assembly Exception [2].
   *
   * [1] https://www.gnu.org/software/classpath/license.html
   * [2] http://openjdk.java.net/legal/assembly-exception.html
   *
   * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
   *******************************************************************************/
  
  #include "omrcfg.h"
  
  #define linkNext _linkNext
  #define linkPrevious _linkPrevious
  
  #include "omrlinkedlist.h"
  #if defined(OMR_GC)
  #include "mminitcore.h"
  #endif /* OMR_GC */
  #include "omrtrace.h"
  #include "ut_omrvm.h"
  #include "omrutil.h"
  #include "OMR_Agent.hpp"
  #include "OMR_Runtime.hpp"
  #include "OMR_VM.hpp"
  #include "OMR_VMThread.hpp"
  
  extern "C" {
  
  omr_error_t
  omr_attach_vm_to_runtime(OMR_VM *vm)
  {
  	omr_error_t rc = OMR_ERROR_NONE;
  	omrthread_t self = NULL;
  
  	if (0 == omrthread_attach_ex(&self, J9THREAD_ATTR_DEFAULT)) {
  		if ((0 == omrthread_tls_alloc(&vm->_vmThreadKey)) &&
  			(0 == omrthread_monitor_init_with_name(&vm->_vmThreadListMutex, 0, "OMR VM thread list mutex"))
  		) {
  			rc = attachVM(vm->_runtime, vm);
  		} else {
  			rc = OMR_ERROR_OUT_OF_NATIVE_MEMORY;
  		}
  		omrthread_detach(self);
  	} else {
  		rc = OMR_ERROR_FAILED_TO_ATTACH_NATIVE_THREAD;
  	}
  
  	return rc;
  
  }
  
  omr_error_t
  omr_detach_vm_from_runtime(OMR_VM *vm)
  {
  	omr_error_t rc = OMR_ERROR_NONE;
  	omrthread_t self = NULL;
  
  	if (0 == omrthread_attach_ex(&self, J9THREAD_ATTR_DEFAULT)) {
  		omrthread_monitor_destroy(vm->_vmThreadListMutex);
  		if (vm->_vmThreadKey > 0) {
  			omrthread_tls_free(vm->_vmThreadKey);
  		}
  		rc = detachVM(vm->_runtime, vm);
  		omrthread_detach(self);
  	} else {
  		rc = OMR_ERROR_FAILED_TO_ATTACH_NATIVE_THREAD;
  	}
  
  	return rc;
  
  }
  
  omr_error_t
  attachThread(OMR_VM *vm, OMR_VMThread *vmthread)
  {
  	omr_error_t rc = OMR_ERROR_NONE;
  	uintptr_t maximum_thread_count = vm->_configuration._maximum_thread_count;
  	uintptr_t internal = vmthread->_internal;
  
  	omrthread_monitor_enter(vm->_vmThreadListMutex);
  	if (internal || (0 == maximum_thread_count) || (vm->_languageThreadCount < maximum_thread_count)) {
  		J9_LINKED_LIST_ADD_LAST(vm->_vmThreadList, vmthread);
  		if (internal) {
  			vm->_internalThreadCount += 1;
  		} else {
  			vm->_languageThreadCount += 1;
  		}
  	} else {
  		rc = OMR_ERROR_MAXIMUM_THREAD_COUNT_EXCEEDED;
  	}
  	/* Set the TLS for vmthread, NOT the current thread */
  	omrthread_tls_set(vmthread->_os_thread, vm->_vmThreadKey, (void *)vmthread);
  	omrthread_monitor_exit(vm->_vmThreadListMutex);
  
  	return rc;
  }
  
  omr_error_t
  detachThread(OMR_VM *vm, OMR_VMThread *vmthread)
  {
  	omr_error_t rc = OMR_ERROR_NONE;
  	uintptr_t internal = vmthread->_internal;
  
  	omrthread_monitor_enter(vm->_vmThreadListMutex);
  	if (internal) {
  		vm->_internalThreadCount -= 1;
  	} else {
  		vm->_languageThreadCount -= 1;
  	}
  	J9_LINKED_LIST_REMOVE(vm->_vmThreadList, vmthread);
  	/* Set the TLS for vmthread, NOT the current thread */
  	omrthread_tls_set(vmthread->_os_thread, vm->_vmThreadKey, NULL);
  	omrthread_monitor_exit(vm->_vmThreadListMutex);
  
  	return rc;
  }
  
  #if defined(OMR_THR_FORK_SUPPORT)
  
  void
  omr_vm_preFork(OMR_VM *omrVM)
  {
  	omrthread_t self = NULL;
  
  	/* We cannot take (and hold) a lock unless the thread is attached. Being unattached is fatal. */
  	intptr_t result = omrthread_attach_ex(&self, J9THREAD_ATTR_DEFAULT);
  	Assert_OMRVM_true(0 == result);
  
  	if (NULL != omrVM->_omrTIAccessMutex) {
  		omrthread_monitor_enter(omrVM->_omrTIAccessMutex);
  	}
  	omrthread_monitor_enter(omrVM->_vmThreadListMutex);
  
  	if (NULL != omrVM->_hcAgent) {
  		omrVM->_hcAgent->callOnPreFork();
  	}
  	omr_trc_preForkHandler();
  	omrthread_lib_preFork();
  }
  
  void
  omr_vm_postForkParent(OMR_VM *omrVM)
  {
  	omrthread_lib_postForkParent();
  	omr_trc_postForkParentHandler();
  	if (NULL != omrVM->_hcAgent) {
  		omrVM->_hcAgent->callOnPostForkParent();
  	}
  	omrthread_monitor_exit(omrVM->_vmThreadListMutex);
  
  	if (NULL != omrVM->_omrTIAccessMutex) {
  		omrthread_monitor_exit(omrVM->_omrTIAccessMutex);
  	}
  	omrthread_detach(NULL);
  }
  
  void
  omr_vm_postForkChild(OMR_VM *omrVM)
  {
  	omrthread_t self = omrthread_self();
  	OMRPORT_ACCESS_FROM_OMRVM(omrVM);
  
  	omrthread_lib_postForkChild();
  
  	omr_trc_postForkChildHandler();
  
  	omrVM->parentPID = omrsysinfo_get_ppid();
  	omrVM->forkGeneration += 1;
  
  	/* Find the current OMR_VMThread and free the rest.
  	 *
  	 * vmThreads hold references to pthread sync objects that may have been corrupted
  	 * by forking, such as the mutex protecting the threadName. The memory for those
  	 * sync objects can't be reused, so they must not be freed. Destroying a vmThread
  	 * normally frees them. We can destroy vmThreads normally here because we have
  	 * already run omrthread_lib_postForkChild() to remove the references to these sync
  	 * objects.
  	 *
  	 * If vmThread cleanup is moved prior to omrthread_lib_postForkChild(), we must ensure
  	 * that pthread sync objects are not freed if they can't be reused.
  	 */
  	OMR_VMThread *currentVMThread = NULL;
  	OMR_VMThread *walkThread = J9_LINKED_LIST_START_DO(omrVM->_vmThreadList);
  	while (NULL != walkThread) {
  		OMR_VMThread *nextThread = J9_LINKED_LIST_NEXT_DO(omrVM->_vmThreadList, walkThread);
  		if (walkThread->_os_thread == self) {
  			currentVMThread = walkThread;
  		} else {
  			void *languageThread = walkThread->_language_vmthread;
  
  #if defined(OMR_GC)
  			if (NULL != omrVM->_gcOmrVMExtensions) {
  				cleanupMutatorModel(walkThread, 1);
  			}
  #endif /* defined(OMR_GC) */
  			setOMRVMThreadNameWithFlagNoLock(walkThread, NULL, FALSE);
  
  			omr_vmthread_destroy(walkThread);
  			omrmem_free_memory(walkThread);
  			OMR_Glue_FreeLanguageThread(languageThread);
  		}
  		walkThread = nextThread;
  	}
  
  	/* Reset vm thread counts and add the current thread to thread list. */
  	omrVM->_vmThreadList = NULL;
  	Assert_OMRVM_true(NULL != currentVMThread);
  	if (currentVMThread->_internal) {
  		omrVM->_internalThreadCount = 1;
  		omrVM->_languageThreadCount = 0;
  	} else {
  		omrVM->_internalThreadCount = 0;
  		omrVM->_languageThreadCount = 1;
  	}
  	J9_LINKED_LIST_ADD_LAST(omrVM->_vmThreadList, currentVMThread);
  
  	if (NULL != omrVM->_hcAgent) {
  		omrVM->_hcAgent->callOnPostForkChild();
  	}
  	omrthread_monitor_exit(omrVM->_vmThreadListMutex);
  
  	if (NULL != omrVM->_omrTIAccessMutex) {
  		omrthread_monitor_exit(omrVM->_omrTIAccessMutex);
  	}
  	Trc_OMRVM_PostForkChild(omrVM->forkGeneration, omrVM->parentPID);
  	omrthread_detach(NULL);
  }
  
  #endif /* defined(OMR_THR_FORK_SUPPORT) */
  
  }
  