안드로이드 개발 정보
(글 수 1,066)
안녕하세요 아즈라엘 입니다.
이번에 JNI 작업중 native thread에서 jni 를 통해 java와 연결을 하는 상황이 발생되었습니다
jint JNI_OnLoad(JavaVM *vm, void *reserved);
를 통해서 JavaVM *vm 을 얻어와 글로벌 변수로 저장을 하여 사용하였고
vm-> AttachCurrentThread() 을 통해 env 를 JVM에 붙여서 사용을 하였습니다.
일단 잘 되는거처럼 보이긴 하는데 문제가 발생되는군요
문제가 뭐냐~!!!
바로 .. AttachCurrentThread 사용시 env를 2개밖에 못넣는다는것....
고로 thread는 두개밖에 못쓴다는거죠..ㅠㅠ
http://xrath.com/javase/ko/6/docs/ko/technotes/guides/jni/spec/invocation.html
<본문인용> .... 네이티브 thread를 2 개의 Java VM 에 동시에 접속할 수 없습니다. ....
미치겠군요..
DetachCurrentThread 해도 리턴값을 친절하게 JNI_ERR를 내려주는군요
여러분은 저와 같은 상황을 겪지 않기를 바라며.. 만약 저보다 먼저 겪어서 해결하신 분은
소중한 조언 부탁드립니다. ^^
해푸미~!!
2012.02.22 10:26:52
(추천:
1 / 0)
해결했습니다. ^^
davik thread정책을 보고 힌트를 얻어서
vm-> AttachCurrentThread() 와 vm-> DetachCurrentThread() 를 타이트 하게 관리 했는데
vm-> AttachCurrentThread() 하였다고 무족건 vm-> DetachCurrentThread() 하면 안됩니다.
반듯이 Thread save 상황에서 AttachCurrentThread ~ DetachCurrentThread 가 한 세트가 되어야 하는군요..스레드 카운트 해서 env 관리 하는거 같습니다. 2개까지 인정해 주는군요..아래 thread.c 소스~~!!
/* 1656 * Attach the current thread to the VM. 1657 * 1658 * Used for internally-created threads and JNI's AttachCurrentThread. 1659 */ 1660 bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon) 1661 { 1662 Thread* self = NULL; 1663 Object* threadObj = NULL; 1664 Object* vmThreadObj = NULL; 1665 StringObject* threadNameStr = NULL; 1666 Method* init; 1667 bool ok, ret; 1668 1669 /* establish a basic sense of self */ 1670 self = allocThread(gDvm.stackSize); 1671 if (self == NULL) 1672 goto fail; 1673 setThreadSelf(self); 1674 1675 /* 1676 * Create Thread and VMThread objects. We have to use ALLOC_NO_GC 1677 * because this thread is not yet visible to the VM. We could also 1678 * just grab the GC lock earlier, but that leaves us executing 1679 * interpreted code with the lock held, which is not prudent. 1680 * 1681 * The alloc calls will block if a GC is in progress, so we don't need 1682 * to check for global suspension here. 1683 * 1684 * It's also possible for the allocation calls to *cause* a GC. 1685 */ 1686 //BUG: deadlock if a GC happens here during HeapWorker creation 1687 threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_NO_GC); 1688 if (threadObj == NULL) 1689 goto fail; 1690 vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_NO_GC); 1691 if (vmThreadObj == NULL) 1692 goto fail; 1693 1694 self->threadObj = threadObj; 1695 dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)self); 1696 1697 /* 1698 * Do some java.lang.Thread constructor prep before we lock stuff down. 1699 */ 1700 if (pArgs->name != NULL) { 1701 threadNameStr = dvmCreateStringFromCstr(pArgs->name, ALLOC_NO_GC); 1702 if (threadNameStr == NULL) { 1703 assert(dvmCheckException(dvmThreadSelf())); 1704 goto fail; 1705 } 1706 } 1707 1708 init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>", 1709 "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); 1710 if (init == NULL) { 1711 assert(dvmCheckException(dvmThreadSelf())); 1712 goto fail; 1713 } 1714 1715 /* 1716 * Finish our thread prep. We need to do this before invoking any 1717 * interpreted code. prepareThread() requires that we hold the thread 1718 * list lock. 1719 */ 1720 dvmLockThreadList(self); 1721 ok = prepareThread(self); 1722 dvmUnlockThreadList(); 1723 if (!ok) 1724 goto fail; 1725 1726 self->jniEnv = dvmCreateJNIEnv(self); 1727 if (self->jniEnv == NULL) 1728 goto fail; 1729 1730 /* 1731 * Create a "fake" JNI frame at the top of the main thread interp stack. 1732 * It isn't really necessary for the internal threads, but it gives 1733 * the debugger something to show. It is essential for the JNI-attached 1734 * threads. 1735 */ 1736 if (!createFakeRunFrame(self)) 1737 goto fail; 1738 1739 /* 1740 * The native side of the thread is ready; add it to the list. 1741 */ 1742 LOG_THREAD("threadid=%d: adding to list (attached)\n", self->threadId); 1743 1744 /* Start off in VMWAIT, because we may be about to block 1745 * on the heap lock, and we don't want any suspensions 1746 * to wait for us. 1747 */ 1748 self->status = THREAD_VMWAIT; 1749 1750 /* 1751 * Add ourselves to the thread list. Once we finish here we are 1752 * visible to the debugger and the GC. 1753 */ 1754 dvmLockThreadList(self); 1755 1756 self->next = gDvm.threadList->next; 1757 if (self->next != NULL) 1758 self->next->prev = self; 1759 self->prev = gDvm.threadList; 1760 gDvm.threadList->next = self; 1761 if (!isDaemon) 1762 gDvm.nonDaemonThreadCount++; 1763 1764 dvmUnlockThreadList(); 1765 1766 /* 1767 * It's possible that a GC is currently running. Our thread 1768 * wasn't in the list when the GC started, so it's not properly 1769 * suspended in that case. Synchronize on the heap lock (held 1770 * when a GC is happening) to guarantee that any GCs from here 1771 * on will see this thread in the list. 1772 */ 1773 dvmLockMutex(&gDvm.gcHeapLock); 1774 dvmUnlockMutex(&gDvm.gcHeapLock); 1775 1776 /* 1777 * Switch to the running state now that we're ready for 1778 * suspensions. This call may suspend. 1779 */ 1780 dvmChangeStatus(self, THREAD_RUNNING); 1781 1782 /* 1783 * Now we're ready to run some interpreted code. 1784 * 1785 * We need to construct the Thread object and set the VMThread field. 1786 * Setting VMThread tells interpreted code that we're alive. 1787 * 1788 * Call the (group, name, priority, daemon) constructor on the Thread. 1789 * This sets the thread's name and adds it to the specified group, and 1790 * provides values for priority and daemon (which are normally inherited 1791 * from the current thread). 1792 */ 1793 JValue unused; 1794 dvmCallMethod(self, init, threadObj, &unused, (Object*)pArgs->group, 1795 threadNameStr, getThreadPriorityFromSystem(), isDaemon); 1796 if (dvmCheckException(self)) { 1797 LOGE("exception thrown while constructing attached thread object\n"); 1798 goto fail_unlink; 1799 } 1800 //if (isDaemon) 1801 // dvmSetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon, true); 1802 1803 /* 1804 * Set the VMThread field, which tells interpreted code that we're alive. 1805 * 1806 * The risk of a thread start collision here is very low; somebody 1807 * would have to be deliberately polling the ThreadGroup list and 1808 * trying to start threads against anything it sees, which would 1809 * generally cause problems for all thread creation. However, for 1810 * correctness we test "vmThread" before setting it. 1811 */ 1812 if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) { 1813 dvmThrowException("Ljava/lang/IllegalThreadStateException;", 1814 "thread has already been started"); 1815 /* We don't want to free anything associated with the thread 1816 * because someone is obviously interested in it. Just let 1817 * it go and hope it will clean itself up when its finished. 1818 * This case should never happen anyway. 1819 * 1820 * Since we're letting it live, we need to finish setting it up. 1821 * We just have to let the caller know that the intended operation 1822 * has failed. 1823 * 1824 * [ This seems strange -- stepping on the vmThread object that's 1825 * already present seems like a bad idea. TODO: figure this out. ] 1826 */ 1827 ret = false; 1828 } else 1829 ret = true; 1830 dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj); 1831 1832 /* These are now reachable from the thread groups. */ 1833 dvmClearAllocFlags(threadObj, ALLOC_NO_GC); 1834 dvmClearAllocFlags(vmThreadObj, ALLOC_NO_GC); 1835 1836 /* 1837 * The thread is ready to go; let the debugger see it. 1838 */ 1839 self->threadObj = threadObj; 1840 1841 LOG_THREAD("threadid=%d: attached from native, name=%s\n", 1842 self->threadId, pArgs->name); 1843 1844 /* tell the debugger & DDM */ 1845 if (gDvm.debuggerConnected) 1846 dvmDbgPostThreadStart(self); 1847 1848 return ret; 1849 1850 fail_unlink: 1851 dvmLockThreadList(self); 1852 unlinkThread(self); 1853 if (!isDaemon) 1854 gDvm.nonDaemonThreadCount--; 1855 dvmUnlockThreadList(); 1856 /* fall through to "fail" */ 1857 fail: 1858 dvmClearAllocFlags(threadObj, ALLOC_NO_GC); 1859 dvmClearAllocFlags(vmThreadObj, ALLOC_NO_GC); 1860 if (self != NULL) { 1861 if (self->jniEnv != NULL) { 1862 dvmDestroyJNIEnv(self->jniEnv); 1863 self->jniEnv = NULL; 1864 } 1865 freeThread(self); 1866 } 1867 setThreadSelf(NULL); 1868 return false; 1869 } 1870 1871 /* 1872 * Detach the thread from the various data structures, notify other threads 1873 * that are waiting to "join" it, and free up all heap-allocated storage. 1874 * 1875 * Used for all threads. 1876 * 1877 * When we get here the interpreted stack should be empty. The JNI 1.6 spec 1878 * requires us to enforce this for the DetachCurrentThread call, probably 1879 * because it also says that DetachCurrentThread causes all monitors 1880 * associated with the thread to be released. (Because the stack is empty, 1881 * we only have to worry about explicit JNI calls to MonitorEnter.) 1882 * 1883 * THOUGHT: 1884 * We might want to avoid freeing our internal Thread structure until the 1885 * associated Thread/VMThread objects get GCed. Our Thread is impossible to 1886 * get to once the thread shuts down, but there is a small possibility of 1887 * an operation starting in another thread before this thread halts, and 1888 * finishing much later (perhaps the thread got stalled by a weird OS bug). 1889 * We don't want something like Thread.isInterrupted() crawling through 1890 * freed storage. Can do with a Thread finalizer, or by creating a 1891 * dedicated ThreadObject class for java/lang/Thread and moving all of our 1892 * state into that. 1893 */ 1894 void dvmDetachCurrentThread(void) 1895 { 1896 Thread* self = dvmThreadSelf(); 1897 Object* vmThread; 1898 Object* group; 1899 1900 /* 1901 * Make sure we're not detaching a thread that's still running. (This 1902 * could happen with an explicit JNI detach call.) 1903 * 1904 * A thread created by interpreted code will finish with a depth of 1905 * zero, while a JNI-attached thread will have the synthetic "stack 1906 * starter" native method at the top. 1907 */ 1908 int curDepth = dvmComputeExactFrameDepth(self->curFrame); 1909 if (curDepth != 0) { 1910 bool topIsNative = false; 1911 1912 if (curDepth == 1) { 1913 /* not expecting a lingering break frame; just look at curFrame */ 1914 assert(!dvmIsBreakFrame(self->curFrame)); 1915 StackSaveArea* ssa = SAVEAREA_FROM_FP(self->curFrame); 1916 if (dvmIsNativeMethod(ssa->method)) 1917 topIsNative = true; 1918 } 1919 1920 if (!topIsNative) { 1921 LOGE("ERROR: detaching thread with interp frames (count=%d)\n", 1922 curDepth); 1923 dvmDumpThread(self, false); 1924 dvmAbort(); 1925 } 1926 } 1927 1928 group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group); 1929 LOG_THREAD("threadid=%d: detach (group=%p)\n", self->threadId, group); 1930 1931 /* 1932 * Release any held monitors. Since there are no interpreted stack 1933 * frames, the only thing left are the monitors held by JNI MonitorEnter 1934 * calls. 1935 */ 1936 dvmReleaseJniMonitors(self); 1937 1938 /* 1939 * Do some thread-exit uncaught exception processing if necessary. 1940 */ 1941 if (dvmCheckException(self)) 1942 threadExitUncaughtException(self, group); 1943 1944 /* 1945 * Remove the thread from the thread group. 1946 */ 1947 if (group != NULL) { 1948 Method* removeThread = 1949 group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread]; 1950 JValue unused; 1951 dvmCallMethod(self, removeThread, group, &unused, self->threadObj); 1952 } 1953 1954 /* 1955 * Clear the vmThread reference in the Thread object. Interpreted code 1956 * will now see that this Thread is not running. As this may be the 1957 * only reference to the VMThread object that the VM knows about, we 1958 * have to create an internal reference to it first. 1959 */ 1960 vmThread = dvmGetFieldObject(self->threadObj, 1961 gDvm.offJavaLangThread_vmThread); 1962 dvmAddTrackedAlloc(vmThread, self); 1963 dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL); 1964 1965 /* clear out our struct Thread pointer, since it's going away */ 1966 dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL); 1967 1968 /* 1969 * Tell the debugger & DDM. This may cause the current thread or all 1970 * threads to suspend. 1971 * 1972 * The JDWP spec is somewhat vague about when this happens, other than 1973 * that it's issued by the dying thread, which may still appear in 1974 * an "all threads" listing. 1975 */ 1976 if (gDvm.debuggerConnected) 1977 dvmDbgPostThreadDeath(self); 1978 1979 /* 1980 * Thread.join() is implemented as an Object.wait() on the VMThread 1981 * object. Signal anyone who is waiting. 1982 */ 1983 dvmLockObject(self, vmThread); 1984 dvmObjectNotifyAll(self, vmThread); 1985 dvmUnlockObject(self, vmThread); 1986 1987 dvmReleaseTrackedAlloc(vmThread, self); 1988 vmThread = NULL; 1989 1990 /* 1991 * We're done manipulating objects, so it's okay if the GC runs in 1992 * parallel with us from here out. It's important to do this if 1993 * profiling is enabled, since we can wait indefinitely. 1994 */ 1995 self->status = THREAD_VMWAIT; 1996 1997 #ifdef WITH_PROFILER 1998 /* 1999 * If we're doing method trace profiling, we don't want threads to exit, 2000 * because if they do we'll end up reusing thread IDs. This complicates 2001 * analysis and makes it impossible to have reasonable output in the 2002 * "threads" section of the "key" file. 2003 * 2004 * We need to do this after Thread.join() completes, or other threads 2005 * could get wedged. Since self->threadObj is still valid, the Thread 2006 * object will not get GCed even though we're no longer in the ThreadGroup 2007 * list (which is important since the profiling thread needs to get 2008 * the thread's name). 2009 */ 2010 MethodTraceState* traceState = &gDvm.methodTrace; 2011 2012 dvmLockMutex(&traceState->startStopLock); 2013 if (traceState->traceEnabled) { 2014 LOGI("threadid=%d: waiting for method trace to finish\n", 2015 self->threadId); 2016 while (traceState->traceEnabled) { 2017 int cc; 2018 cc = pthread_cond_wait(&traceState->threadExitCond, 2019 &traceState->startStopLock); 2020 assert(cc == 0); 2021 } 2022 } 2023 dvmUnlockMutex(&traceState->startStopLock); 2024 #endif 2025 2026 dvmLockThreadList(self); 2027 2028 /* 2029 * Lose the JNI context. 2030 */ 2031 dvmDestroyJNIEnv(self->jniEnv); 2032 self->jniEnv = NULL; 2033 2034 self->status = THREAD_ZOMBIE; 2035 2036 /* 2037 * Remove ourselves from the internal thread list. 2038 */ 2039 unlinkThread(self); 2040 2041 /* 2042 * If we're the last one standing, signal anybody waiting in 2043 * DestroyJavaVM that it's okay to exit. 2044 */ 2045 if (!dvmGetFieldBoolean(self->threadObj, gDvm.offJavaLangThread_daemon)) { 2046 gDvm.nonDaemonThreadCount--; // guarded by thread list lock 2047 2048 if (gDvm.nonDaemonThreadCount == 0) { 2049 int cc; 2050 2051 LOGV("threadid=%d: last non-daemon thread\n", self->threadId); 2052 //dvmDumpAllThreads(false); 2053 // cond var guarded by threadListLock, which we already hold 2054 cc = pthread_cond_signal(&gDvm.vmExitCond); 2055 assert(cc == 0); 2056 } 2057 } 2058 2059 LOGV("threadid=%d: bye!\n", self->threadId); 2060 releaseThreadId(self); 2061 dvmUnlockThreadList(); 2062 2063 setThreadSelf(NULL); 2064 freeThread(self); 2065 }
JNI_CreateJavaVM 사용 못함 ...
jni.h 보니깡 아래와 같이 생겼네요..ㅠㅠ
http://stackoverflow.com/questions/8145985/android-ndk-cant-find-jni-getcreatedjavavms-after-update-from-r6-to-r7