Java에서는 selective compile이 되지 않는다.

대신, compile time에 결정되는 것들을 이용해서 selective compile과 비슷한 효과를  만들어 낼 수 있다.

예를 들면,


Config.jar
----------
class Config {
...
    public static final boolean DEBUG = false;
...
}


Main.java
---------
...
if (Config.DEBUG) System.out.println("debug line...");
...

자, 그러면 어디서 어디까지 Compile time 에 결정되는가?

openJDK 1.6에서 test를 해 보면, 아래의 것들에 대해서는 compile time에 결정되는다는 것을 확인했다.


primitive types (int, boolean, long...) + String.


여기서 의외의 부분이 'String'인데...

실제, 아래와 같이 static final String 변수로 test해 보면, 이것을 확인할 수 있다.

Config.jar
----------
class Config {
    ...
    public static final String MODE = "debug";
    ...
}


Main.java
---------
...
if (Config.MODE.equals("debug")) System.out.println("debug mode...");
...

생각해보면, String class자체가 constant class이므로 - 한번 생성되면, 내용이 바뀌지 않는 class - 위와 같은 compile time check가 가능하지 않을까... 생각이 든다.


아래는 위의 내용에 대한 상세 test이다.

test/lib/Config.java -------------------- package test.lib; public class Config { public static final int TEST_INT_0 = 1; public static final boolean TEST_BOOL_0 = false; public static final String TEST_STR_0 = "test-str-0"; public static int TESTV_INT_0 = 1; public static boolean TESTV_BOOL_0 = false; public static String TESTV_STR_0 = "test-str-0"; } ===> export to 'test0.jar' package test.lib; public class Config { public static final int TEST_INT_0 = 10; public static final boolean TEST_BOOL_0 = true; public static final String TEST_STR_0 = "test-str-1"; public static int TESTV_INT_0 = 10; public static boolean TESTV_BOOL_0 = true; public static String TESTV_STR_0 = "test-str-1"; } ===> export to 'test1'jar Main.java ---------- import test.lib.Config; public class Main { public static void main(String[] args) { if (Config.TEST_STR_0.equals("test-str-0")) System.out.println("Config:TEST_STR_0 : IF path - test-str-0"); else System.out.println("Config:TEST_STR_0 : ELSE path - test-str-0"); if (Config.TEST_INT_0 > 5) System.out.println("Config:TEST_INT_0 : IF path - > 5"); else System.out.println("Config:TEST_INT_0 : ELSE path - > 5"); if (Config.TEST_BOOL_0) System.out.println("Config:TEST_BOOL_0 : IF path - true"); else System.out.println("Config:TEST_BOOL_0 : ELSE path - "); if (Config.TESTV_STR_0.equals("test-str-0")) System.out.println("Config:TESTV_STR_0 : IF path - test-str-0"); else System.out.println("Config:TESTV_STR_0 : ELSE path - test-str-0"); if (Config.TESTV_INT_0 > 5) System.out.println("Config:TESTV_INT_0 : IF path - > 5"); else System.out.println("Config:TESTV_INT_0 : ELSE path - > 5"); if (Config.TESTV_BOOL_0) System.out.println("Config:TESTV_BOOL_0 : IF path - true"); else System.out.println("Config:TESTV_BOOL_0 : ELSE path - "); } } ========================================== $ javac -classpath test0.jar Main.java $ java -classpath .:test0.jar Main Config:TEST_STR_0 : IF path - test-str-0 Config:TEST_INT_0 : ELSE path - > 5 Config:TEST_BOOL_0 : ELSE path - true Config:TESTV_STR_0 : IF path - test-str-0 Config:TESTV_INT_0 : ELSE path - > 5 Config:TESTV_BOOL_0 : ELSE path - true $ $ java -classpath .:test1.jar Main Config:TEST_STR_0 : IF path - test-str-0 <--- 바뀌지 않음. (변수 값이 compile time에 이미 binding되어 있음.) Config:TEST_INT_0 : ELSE path - > 5 <--- 상동 Config:TEST_BOOL_0 : ELSE path - true <--- 상동 Config:TESTV_STR_0 : ELSE path - test-str-0 Config:TESTV_INT_0 : IF path - > 5 Config:TESTV_BOOL_0 : IF path - 참고 : TESTV_XXX_0를 없애면, test0.jar 혹은 test1. jar를 'java'의 'classpath' 에 명시해 주지 않더라도 정상수행 된다. ex. $ java Main 왜냐하면, 이미 모든 code가 compile time에 binding어 있는 상태으므로, Runtime에는 더 이상 'test.lib.Config' 를 참조하지 않기 때문이다.

위와 같은 static final 변수를 runtime binding하기 위한 방법의 한 예로 reflection을 들 수 있다.

reflection을 이용해서 해당 변수 field를 접근하면, static final로 선언된 변수이지만, runtime binding이 가능하다.

일단 man page를 유심히 살펴보자.

FIFO(7)                             Linux Programmer's Manual                            FIFO(7)

NAME
       fifo - first-in first-out special file, named pipe

DESCRIPTION
       A  FIFO  special  file (a named pipe) is similar to a pipe, except that it is accessed as
       part of the file system.  It can be opened by multiple processes for reading or  writing.
       When  processes  are  exchanging data via the FIFO, the kernel passes all data internally
       without writing it to the file system.  Thus, the FIFO special file has  no  contents  on
       the  file  system;  the file system entry merely serves as a reference point so that pro‐
       cesses can access the pipe using a name in the file system.

       The kernel maintains exactly one pipe object for each FIFO special file that is opened by
       at  least one process.  The FIFO must be opened on both ends (reading and writing) before
       data can be passed.  Normally, opening the FIFO blocks until  the  other  end  is  opened
       also.

       A  process can open a FIFO in nonblocking mode.  In this case, opening for read only will
       succeed even if no-one has opened on the write side yet, opening for write only will fail
       with ENXIO (no such device or address) unless the other end has already been opened.

       Under  Linux,  opening  a  FIFO for read and write will succeed both in blocking and non‐
       blocking mode.  POSIX leaves this behavior undefined.  This can be used to  open  a  FIFO
       for  writing  while there are no readers available.  A process that uses both ends of the
       connection in order to communicate with itself should be very careful to avoid deadlocks.

NOTES
       When a process tries to write to a FIFO that is not opened for read on  the  other  side,
       the process is sent a SIGPIPE signal.

       FIFO  special files can be created by mkfifo(3), and are indicated by ls -l with the file
       type 'p'.

SEE ALSO
       mkfifo(1), open(2), pipe(2), sigaction(2), signal(2), socketpair(2), mkfifo(3), pipe(7)

COLOPHON
       This page is part of release 3.35 of the Linux man-pages project.  A description  of  the
       project, and information about reporting bugs, can be found at http://man7.org/linux/man-
       pages/.

Linux                                      2008-12-03                                    FIFO(7)

주의할 사항은 위에서, Bold로 표시해 두었다.


먼저 "read/write 양쪽이 열리기 전까지는 open이 block된다."는 말이 무슨 말일까?

아래 코드를 살펴보자.

int main()
{
	char msg[64];
	int fd;
	int rd;

	if (-1 == mkfifo("./fifo",0666))
		return 1;

	if (-1 == (fd = open("./fifo", O_RDWR))) // <--- (*A)
		return -1;
	printf("Before loop\n"); // <--- (*a)
	for (;;) {
		if (-1 == (rd = read(fd, msg, sizeof(msg))))
			return 1;
	}
	unlink("./fifo");
	return 0;
}

위의 코드는 실행시키면 바로 , (*a)가 수행된다.

그렇지만, (*A)의 open mode를 O_WRONLY 나 O_RDONLY 로 하면, pipe의 다른 한쪽 (read 혹은 write)가 열리기 전까지는 (*a)가 수행되지 않는다 (open에서 block된 상태) 는 말이다.



그리고, "read only의 경우 nonblocking mode로 open이 가능하다고 wirte는 안된다." 는 어떤식 문제를 가져 오는가?

앞서 이야기한 것처럼, 위의 코드에서, (*A)를 O_RDONLY로 하면, write pipe가 열리기 전까지는 (*a)가 수행되지 않는다.(block된 상태).

그렇지만, "$ echo hello > ./fifo" 같은 명령을 통해 write side를 일단 열개 되면, loop로 들어가게 되는데,

문제는, read가 NONBLOCKING으로 동작한다는 것이다.

즉, 더 이상 읽을 것이 없음에도 불구하고, block이 되지 않고 busy loop를 돌게 된다.
하지만, 위의 예시처럼, O_RDWR로 열게 되면, 더 이상 읽을게 없을 경우, read 에서 block 되어, 일반적으로 기대하는 방식으로 동작하게 된다.


추가적으로, File이란, System-wide한 resource이다.

따라서, 두 개 이상의 Process가 하나의 Pipe에 대해 동시에 read를 하고 있다면, 먼저 읽는 Process가 Pipe의 내용을 읽어가게 되고, 다른  Process들은 내용을 읽지 못한다.


'Language > C&C++' 카테고리의 다른 글

'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25
[c/c++] file copy using 'sendfile' on linux.  (0) 2013.12.17
일반 file에 대한 select/poll...  (0) 2013.11.14
Using allocated memory like 2-D array.  (0) 2013.07.10

Since Linux 2.6.33, sendfile supports regular file as output file.
That is, sendfile can be used as way for fast file copy.
(Faster than normal read -> write operation because it is done inside kernel! )
Please refer follow example code ...

...


#include <stdio.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int
main(int argc, const char *argv[]) {
	int ifd, ofd, r;
	struct stat st;

	r = stat(argv[2], &st);
	r |= (ofd = open(argv[1], O_WRONLY | O_CREAT));
	r |= (ifd = open(argv[2], O_RDONLY));
	if (r < 0)
		return 1;
	if (-1 == sendfile(ofd, ifd, 0, st.st_size))
		return 1;
	fchmod(ofd, 0664);
	return 0;
}


...

selectpollpipe등에 대해서 block하면서 event(읽을 수 있게 되는 상태)를 기다리기 위해서 많이 쓴다.

man page를 보면...

       select()  and pselect() allow a program to monitor multiple file descriptors, waiting until
       one or more of the file descriptors become "ready" for some class of I/O  operation  (e.g.,
       input  possible).   A  file descriptor is considered ready if it is possible to perform the
       corresponding I/O operation (e.g., read(2)) without blocking.

그렇지만 자칫 오해하면, 일반 파일에 대해서 새로 추가된 내용이 있는지 검사하고 새로 추가된 내용을 읽어들이기 위해서 selectpoll을 사용할 수 있을 거라고 기대할 수도 있다.

그렇지만, select/poll은 man page에 언급한 바처럼, "IO가 block되지 않고 IO를 행할 수 있는 상태"를 기다린다.

file descriptorread() operation을 통해서 EOF에 도달하더라도, read() operation은 blocking없이 계속해서 수행가능하다는 점 - 비록 계속 0 byte를 읽겠지만... - 을 생각해보면, 위와 같은 생각은 적합하지 않다는 것을 알 수 있다.

주의하자!


아래는 간단한 test code이다. 1초마다 계속해서 read() 가 정상 수행됨을 알 수 있다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <errno.h>

int
main(int argc, const char *argv[]) {
	char buf[4096];
	fd_set rfds;
	struct timeval tv;
	int fd, rb, retval;

	if (-1 == (fd = open("tstf", O_RDONLY)))
		perror("open tstf");

	/* Wait up to five seconds. */
	tv.tv_sec = 60;
	tv.tv_usec = 0;

	while (1) {
		/* Watch stdin (fd 0) to see when it has input. */
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);

		retval = select(fd + 1, &rfds, NULL, NULL, &tv);
		/* Don't rely on the value of tv now! */

		if (retval == -1)
			perror("select()");
		else if (retval) {
			printf("Data is available now.\n");
			memset(buf, 0, sizeof(buf));
			if (-1 == (rb = read(fd, buf, sizeof(buf) - 1)))
				fprintf(stderr, "%s", strerror(errno));
			printf("%s", buf);
			if (rb < (sizeof(buf) - 1))
				printf("\n---------------------------\nReaches to EOF\n");

			/* FD_ISSET(0, &rfds) will be true. */
		} else
			printf("No data within 1 minutes.\n");
		sleep(1);
	}
	exit(EXIT_SUCCESS);
}

DONE.

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class Main {
    private static volatile int sCnt = 0;
    private static class TestRun implements Runnable {
        @Override
        public void
        run() {
            int num = sCnt++;
            System.out.println("Run(" + num + ") - Start");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ignored) { }
            System.out.println("Run(" + num + ") - End");
        }
    };
    public static void
    main(String[] args) {
        ThreadPoolExecutor tpe = new ThreadPoolExecutor
                (2,
                 2,
                 Long.MAX_VALUE,
                 TimeUnit.MILLISECONDS,
                 new LinkedBlockingQueue());
        int num = 10;
        while (0 < num--)
            tpe.execute(new TestRun());
    }
}

int (*arr)[SZ] = (int (*)[SZ])malloc(sizeof(int) * SZ * SZ);


이것은 function pointer와 비슷하게 이해하면 되는데...


int(*)[SZ]  <=> void(*)(int, char)

int(*arr)[SZ] <=> void (*func)(int, char)


int [SZ] array에 대한 pointer type => int(*)[SZ]

void (int, char) function에 대한 function pointer => void(*)(int, char)

Usually, parent thread who creates child thread, wants to get execution result (return value) of child thread.

For this, pthread_join() is used.

In most cases, pthread_create() / pthread_join() are used like a kind of pair.

So, habitually, developer tends to ignoring about detaching child thread.


But, in some cases, parent thread doesn't care about the result of child thread, and pthread_join() is not used.

In this case, child thread should be detached from parent thread to say that "parent thread doesn't care about the result".

Why this is required?

To pass return value of child thread, some  memory should be used to store result until it is consumed.

So, detaching means avoiding keep that memory.


In POSIX thread, there are two ways for detaching thread.

one is using pthread_detach() function, and the other is using PTHREAD_CREATE_DETACHED attribute at pthread_create().


Always keep in mind that child thread keep some amount of memory to pass it's return value to parent.


Process version of this case is "Zombie Process". See this post for details.

#define pr(a, b...) printf(a, b) /* (*A) */ #define pr(a, b...) printf(a, ##b) /* (*B) */ pr("Hello\n"); /* (*1) */ pr("Hello %s\n", "World"); /* (*2) */ pr("Hello %s %s\n", "My", "World"); /* (*3) */


GCC(C89)에서 사용할 수 있는 위의 두가지 macro는 언뜻 같아 보이지만 차이가 존재한다.
(*A) (*B) 모두 (*2)와 (*3) 같은 경우에서 정상적으로 잘 동작한다.
그런데, (*1)의 경우를 보면, 1은 macro에서 정의하고 있는 두번째 arguement가 없는 경우다. 즉 macro argument 'b'가 없다.
따라서 preprocessing결과를 생각해보면, (*A) (*B) 모두

printf(a, )

가 되어서 compile error가 나야 할 것 같은데, (*A)와 같이 정의하면, compile error가 발생하고, (*B)와 같이 정의하면 정상동작한다.
GCC spec.을 살펴보지 않았으나, 주의할 필요가 있어서 적어 둔다. (재미있지 않은가?)

변수는 사용하기 직전에 선언하는 것이 원칙인데.. 문제는 scope다.
C/C++/JAVA에서 명시적으로 '{ }'를 통해서 scope를 잡아주지 않으면, 이후에도 계속 해당 변수가 살아있는 상태가 되어서 좋지 못하다.
그래서 '{ }'를 사용해서 scope를 제한해 주는 것이 좋은데, 그렇게 하면, indentation에서 괜시리 한칸 들여쓰여지게 되어 미관상 - 개인적으로 - 마음에 안든다...
음...
변수의 scope를 위한 이~~쁜~~ syntax가 있었으면 좋았을텐데... 라는 생각이 그냥 들어서...

There are two simple examples for this.

#ifdef A
#define xxx(...) BBBB(__VA_ARGS__)
#else
#define xxx(...)
#endif

vs.

#ifdef A
#define xxx(...) BBBB(__VA_ARGS__)
#else
static inline void xxx(){}
#endif

I preferred the second one because at the first case, sometimes unexpected problems are issued. (Just personal opinion/preference...)

+ Recent posts