Removed the old, historical and no longer working IPC support.
This commit is contained in:
parent
3dff7bb842
commit
e32a317b76
|
@ -51,7 +51,6 @@ LIBSUBDIRS = \
|
|||
graphics/metafiledrv \
|
||||
graphics/psdrv \
|
||||
graphics/ttydrv \
|
||||
ipc \
|
||||
library \
|
||||
loader \
|
||||
loader/ne \
|
||||
|
@ -127,7 +126,6 @@ LIBOBJS = \
|
|||
graphics/metafiledrv/metafiledrv.o \
|
||||
graphics/psdrv/psdrv.o \
|
||||
graphics/ttydrv/ttydrv.o \
|
||||
ipc/ipc.o \
|
||||
loader/loader.o \
|
||||
loader/ne/ne.o \
|
||||
loader/dos/dos.o \
|
||||
|
|
|
@ -31,10 +31,6 @@ AC_ARG_ENABLE(lib,
|
|||
[ --disable-lib build the Wine without building libwine.a],
|
||||
[if test "$enableval" = "no"; then ALT_LINK="\$(LIBOBJS) \$(X11OBJS)"; LIB_TARGET=""; fi])
|
||||
|
||||
dnl AC_ARG_WITH(ipc,
|
||||
dnl [ --enable-ipc use inter-process communication for DDE],
|
||||
dnl [if test "$enableval" = "no"; then : ; else OPTIONS="-DCONFIG_IPC"; fi])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --disable-debug compile out all debugging messages],
|
||||
[if test "$enableval" = "no"; then DEBUG_MSGS="no"; fi])
|
||||
|
@ -785,7 +781,6 @@ graphics/win16drv/Makefile
|
|||
graphics/x11drv/Makefile
|
||||
if1632/Makefile
|
||||
include/Makefile
|
||||
ipc/Makefile
|
||||
library/Makefile
|
||||
libtest/Makefile
|
||||
loader/Makefile
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#define __WINE_DDE_H
|
||||
|
||||
#include "windef.h"
|
||||
#include "dde_proc.h"
|
||||
|
||||
#define WM_DDE_INITIATE 0x3E0
|
||||
#define WM_DDE_TERMINATE 0x3E1
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_atom.h
|
||||
* Purpose : atom functionality for DDE
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef __WINE_DDE_ATOM_H
|
||||
#define __WINE_DDE_ATOM_H
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include "windef.h"
|
||||
|
||||
#define DDE_ATOMS 157 /* a prime number for hashing */
|
||||
|
||||
void ATOM_GlobalInit(void);
|
||||
|
||||
ATOM DDE_GlobalAddAtom( SEGPTR str );
|
||||
ATOM DDE_GlobalDeleteAtom( ATOM atom );
|
||||
ATOM DDE_GlobalFindAtom( SEGPTR str );
|
||||
WORD DDE_GlobalGetAtomName( ATOM atom, LPSTR buffer, short count );
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
#endif __WINE_DDE_ATOM_H
|
|
@ -1,39 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_mem.h
|
||||
* Purpose : shared DDE memory functionality for DDE
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef __WINE_DDE_MEM_H
|
||||
#define __WINE_DDE_MEM_H
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include "windef.h"
|
||||
#include "global.h"
|
||||
#include "shm_block.h"
|
||||
|
||||
#define DDE_HANDLES 0x0400
|
||||
#define is_dde_handle(block) ( (block) >= (1<<15) && (block) < (1<<15)+DDE_HANDLES )
|
||||
typedef struct {
|
||||
int shmid;
|
||||
REL_PTR rel;
|
||||
}DDE_HWND;
|
||||
|
||||
WORD DDE_SyncHandle(HGLOBAL16 handle, WORD sel);
|
||||
void *DDE_malloc(unsigned int flags,unsigned long size, SHMDATA *shmdata);
|
||||
HANDLE16 DDE_GlobalReAlloc(WORD,long,WORD);
|
||||
HGLOBAL16 DDE_GlobalFree(HGLOBAL16 block);
|
||||
void *DDE_AttachHandle(HGLOBAL16 handle, SEGPTR *segptr);
|
||||
WORD DDE_GlobalHandleToSel( HGLOBAL16 handle );
|
||||
int DDE_GlobalUnlock(int);
|
||||
HANDLE16 DDE_GlobalSize(WORD);
|
||||
HANDLE16 DDE_GlobalHandle(WORD);
|
||||
HANDLE16 DDE_GlobalFlags(WORD);
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
#endif /* __WINE_DDE_MEM_H */
|
|
@ -1,63 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_proc.h
|
||||
* Purpose : DDE signals and processes functionality for DDE
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef __WINE_DDE_PROC_H
|
||||
#define __WINE_DDE_PROC_H
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <setjmp.h>
|
||||
#include "windef.h"
|
||||
#include "windows.h"
|
||||
#define DDE_PROCS 64
|
||||
#define DDE_WINDOWS 64
|
||||
struct _dde_proc {
|
||||
int msg; /* message queue for this process */
|
||||
int shmid; /* first shared memory block id. */
|
||||
int sem; /* semaphore for fragment allocation */
|
||||
int pid;
|
||||
} ;
|
||||
typedef struct _dde_proc *dde_proc;
|
||||
|
||||
extern sigjmp_buf env_wait_x;
|
||||
enum stop_wait_op { /* The action to be taken upon SIGUSR2 */
|
||||
CONT, /* Don't do anything */
|
||||
STOP_WAIT_ACK, /* Use siglongjmp to stop wait_ack() */
|
||||
STOP_WAIT_X /* siglongjmp to stop MSG_WaitXEvent() */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
WORD proc_idx; /* index into wine's process table */
|
||||
HWND16 wnd; /* Window on the local proccess */
|
||||
} WND_DATA;
|
||||
extern enum stop_wait_op stop_wait_op;
|
||||
extern int had_SIGUSR2;
|
||||
|
||||
extern int curr_proc_idx;
|
||||
void stop_wait(int a); /* signal handler for SIGUSR2
|
||||
(interrupts "select" system call) */
|
||||
void dde_proc_init(dde_proc proc); /* init proc array */
|
||||
void dde_proc_done(dde_proc proc); /* delete a proc entry */
|
||||
void dde_proc_refresh(dde_proc proc); /* delete entry, if old junk */
|
||||
void dde_proc_add(dde_proc proc); /* Add current proc to proc array */
|
||||
void dde_msg_setup(int *msg_ptr);
|
||||
int dde_reschedule();
|
||||
void dde_wnd_setup(); /* setup Data structure of DDE windows */
|
||||
|
||||
/* Send ack. to hnd indicating that posted/sent msg. got to destination*/
|
||||
void dde_proc_send_ack(HWND16 wnd, BOOL val);
|
||||
BOOL DDE_PostMessage( MSG16 *msg);
|
||||
BOOL DDE_SendMessage( MSG16 *msg);
|
||||
int DDE_GetRemoteMessage();
|
||||
void DDE_DestroyWindow(HWND16 hwnd); /* delete DDE info regarding hwnd */
|
||||
void DDE_TestDDE(HWND16 hwnd); /* do we have dde handling in the window ?*/
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
#endif /* __WINE_DDE_PROC_H */
|
|
@ -1,90 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_block.ch
|
||||
* Purpose: treat a shared memory block.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef __WINE_SHM_BLOCK_H
|
||||
#define __WINE_SHM_BLOCK_H
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <sys/shm.h>
|
||||
#include "windef.h"
|
||||
#define SEGSIZE 0x10000 /* 64 */
|
||||
#define SHM_GRANULARITY SEGSIZE
|
||||
#define SHM_MINBLOCK SHM_GRANULARITY
|
||||
#define SHM_MAXBLOCK (((int)SHMMAX/(int)SHM_GRANULARITY)* \
|
||||
SHM_GRANULARITY)
|
||||
#define PTR2REL(block,ptr) (REL_PTR) ( (char *) (ptr) - (char *) (block) )
|
||||
#define REL2PTR(block,rel) (void *) ( (char *) (block) + (rel) )
|
||||
|
||||
typedef int REL_PTR;
|
||||
|
||||
/* full info for each shm block. */
|
||||
struct shm_block {
|
||||
/* private */
|
||||
int next_shm_id; /* IPC shm ID (for initial linking) */
|
||||
|
||||
/* public (read only) */
|
||||
int size; /* size of the shm block */
|
||||
int free; /* how much of the block is free */
|
||||
int proc_idx; /* The index of the owner */
|
||||
|
||||
/* public - writable for shm_fragment */
|
||||
REL_PTR free_list; /* first item in the free list */
|
||||
};
|
||||
|
||||
/* used for mapping local attachments */
|
||||
struct local_shm_map {
|
||||
struct local_shm_map *next;
|
||||
int shm_id;
|
||||
int proc_idx;
|
||||
|
||||
/* 32 bit pointer to the beginning of the block */
|
||||
struct shm_block *ptr;
|
||||
};
|
||||
extern struct local_shm_map *shm_map;
|
||||
void shm_setup_block(struct shm_block *block, REL_PTR first, int size);
|
||||
|
||||
/* shm_create_block:
|
||||
* allocate and setup a new block:
|
||||
* first - first non header byte.
|
||||
* size - block size (in bytes).
|
||||
* shm_id- IPC shared memory ID.
|
||||
*/
|
||||
struct shm_block *shm_create_block(REL_PTR first, int size, int *shm_id);
|
||||
|
||||
/* shm_locate_block:
|
||||
* locate existing block according to shm_id,
|
||||
* Attach the block if needed. Assume the shm_id is wine's
|
||||
* Set selectors also.
|
||||
*/
|
||||
struct shm_block *shm_locate_block(int shm_id, struct local_shm_map *map);
|
||||
|
||||
/* shm_locate_attached_block:
|
||||
* locate existing block according to shm_id,
|
||||
* Blocks are never attached.
|
||||
* if proc_idx is not NULL, it will be set to owner's index.
|
||||
* map - localy mapped info about block may be NULL;
|
||||
*/
|
||||
struct shm_block *shm_locate_attached_block(int shm_id,
|
||||
struct local_shm_map *map);
|
||||
|
||||
/* shm_attach_block: attach existing shm block, setup selectors
|
||||
* shm_id - id of the block to attach.
|
||||
* proc_idx - if not -1, puts this data into local mapping
|
||||
* map - localy mapped info about this block. (may be NULL)
|
||||
* NOTE: same block can be attached many times
|
||||
*/
|
||||
struct shm_block *shm_attach_block(int shm_id, int proc_idx,
|
||||
struct local_shm_map *map);
|
||||
|
||||
/* delete chain of shm blocks (pointing to each other */
|
||||
void shm_delete_chain(int *shmid);
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
#endif /* __WINE_SHM_BLOCK_H */
|
|
@ -1,51 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_fragment.h
|
||||
* Purpose: Data fragments and free list items. Allocate and free blocks.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef __WINE_SHM_FRAGMENT_H
|
||||
#define __WINE_SHM_FRAGMENT_H
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include "shm_block.h"
|
||||
|
||||
#define NIL ((int) 0)
|
||||
/* memory fragment: used or free (when free - it's an item of "free list",
|
||||
* when allocated it contains the data, and it's size)
|
||||
*/
|
||||
struct shm_fragment {
|
||||
int size; /* fragment's size */
|
||||
|
||||
/* The type of info depends on fragment's status (free/allocated) */
|
||||
union info {
|
||||
int next; /* next free fragment */
|
||||
char data[1]; /* the data */
|
||||
} info;
|
||||
};
|
||||
|
||||
/* setup first item in the free list */
|
||||
void shm_FragmentInit(struct shm_block *block,REL_PTR first,int size);
|
||||
|
||||
/* allocate shm fragment. return: offset to data in fragment, or NULL */
|
||||
REL_PTR shm_FragmentAlloc(struct shm_block *block, int size);
|
||||
|
||||
/* like shm_FragmentAlloc, returns pointer instead of offset */
|
||||
char *shm_FragPtrAlloc(struct shm_block *block, int size);
|
||||
|
||||
/* free shm fragment - according to offset */
|
||||
void shm_FragmentFree(struct shm_block *block, int ofs);
|
||||
|
||||
/* free shm fragment - according to pointer */
|
||||
void shm_FragPtrFree(struct shm_block *block, void *ptr);
|
||||
|
||||
/* This is used for debugging only */
|
||||
void shm_print_free_list(struct shm_block *block);
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
#endif /* __WINE_SHM_FRAGMENT_H */
|
|
@ -1,55 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_main_blk.h
|
||||
* Purpose: Main Wine's shared memory block
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef __WINE_SHM_MAIN_BLK_H
|
||||
#define __WINE_SHM_MAIN_BLK_H
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <sys/shm.h>
|
||||
#include "shm_block.h"
|
||||
#include "shm_semaph.h"
|
||||
#include "dde_proc.h"
|
||||
#include "dde_atom.h"
|
||||
#include "dde_mem.h"
|
||||
/*****************************************************************************
|
||||
*
|
||||
* main block object
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
#define DDE_HANDLES_BIT_ARRAY_SIZE (DDE_HANDLES/sizeof(int)/8)
|
||||
|
||||
#define SHM_MAXID SHMSEG /* maximum shm blocks (Wine's limit) */
|
||||
struct shm_main_block {
|
||||
/* NOTE: "block" declaration must be the first */
|
||||
struct shm_block block;
|
||||
char magic[64]; /* magic string to identify the block */
|
||||
int build_lock; /* =1 when data structure not stable yet */
|
||||
shm_sem sem; /* semaphores for main_block integrity */
|
||||
struct _dde_proc proc[DDE_PROCS]; /* information about processes */
|
||||
REL_PTR atoms[DDE_ATOMS]; /* relative reference to global atoms */
|
||||
/* Translation from global window handles to local handles */
|
||||
WND_DATA windows[DDE_WINDOWS];
|
||||
DDE_HWND handles[DDE_HANDLES];
|
||||
/* bit array stating if a handle is free (bit=0), LSB in */
|
||||
/* free_handles[0] refers handle 0x8000, the MSB refers 0x801F */
|
||||
unsigned free_handles[DDE_HANDLES_BIT_ARRAY_SIZE];
|
||||
};
|
||||
extern struct shm_main_block *main_block;
|
||||
int shm_init(void);
|
||||
void shm_delete_all(int shm_id);
|
||||
void DDE_mem_init();
|
||||
int DDE_no_of_attached();
|
||||
#define DDE_IPC_init() ( (main_block==NULL) ? (DDE_mem_init()) : 0 )
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
#endif /* __WINE_SHM_MAIN_BLK_H */
|
|
@ -1,28 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_semaph.h
|
||||
* Purpose: Handle semaphores for shared memory operations.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __WINE_SHM_SEMAPH_H
|
||||
#define __WINE_SHM_SEMAPH_H
|
||||
/* IMPORTANT: If possible, restrict usage of these functions. */
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
typedef int shm_sem;
|
||||
|
||||
void shm_read_wait(shm_sem semid);
|
||||
void shm_write_wait(shm_sem semid);
|
||||
void shm_write_signal(shm_sem semid);
|
||||
void shm_read_signal(shm_sem semid);
|
||||
void shm_sem_init(shm_sem *semptr);
|
||||
void shm_sem_done(shm_sem *semptr);
|
||||
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
#endif /* __WINE_SHM_SEMAPH_H */
|
|
@ -1 +0,0 @@
|
|||
Makefile
|
|
@ -1,23 +0,0 @@
|
|||
DEFS = @DLLFLAGS@ -D__WINE__
|
||||
TOPSRCDIR = @top_srcdir@
|
||||
TOPOBJDIR = ..
|
||||
SRCDIR = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
MODULE = ipc
|
||||
|
||||
C_SRCS = \
|
||||
bit_array.c \
|
||||
dde_atom.c \
|
||||
dde_mem.c \
|
||||
dde_proc.c \
|
||||
generic_hash.c \
|
||||
shm_block.c \
|
||||
shm_fragment.c \
|
||||
shm_main_blk.c \
|
||||
shm_semaph.c
|
||||
|
||||
all: $(MODULE).o
|
||||
|
||||
@MAKE_RULES@
|
||||
|
||||
### Dependencies:
|
34
ipc/README
34
ipc/README
|
@ -1,34 +0,0 @@
|
|||
Technion- Israel Institute of Technology
|
||||
Electrical Engineering software lab.
|
||||
|
||||
|
||||
|
||||
Author : Michael Veksler 11678223
|
||||
|
||||
Tutor: Avner Lottem
|
||||
|
||||
|
||||
DDE support for Wine
|
||||
====================
|
||||
|
||||
Wine emulator is able to run MS-Windows 3.1 applications under Linux
|
||||
(Linux is a UNIX clone) . Wine is also a toolkit allowing MS-Windows
|
||||
applications to compile under Linux. Wine is a world-wide Internet
|
||||
project with more than 20 developers. Wine is still under development
|
||||
so many vital MS-Windows features are still missing.
|
||||
|
||||
DDE is Dynamic Data Exchange used for communication between MS-Windows
|
||||
applications. This project adds DDE capabilities into Wine. The new
|
||||
improved Wine will be able to send messages and share memory between
|
||||
two different Wine processes.
|
||||
|
||||
This project uses System-V IPC mechanisms to implement DDE.
|
||||
|
||||
Besides sharing memory handles and sending DDE messages, Wine processes
|
||||
are synchronize to emulates MS-Windows non-preemptive task switching.
|
||||
(MS-Windows switches tasks only on given instructions). This is done
|
||||
without hurting the performance or stability of a single Wine process.
|
||||
|
||||
The project adds DDE to Wine emulator and to the toolkit. This allows
|
||||
old MS-Windows applications to be compiled for UNIX, and have DDE
|
||||
working among them.
|
|
@ -1,42 +0,0 @@
|
|||
After shm_FragmentInit
|
||||
{0x0020,0xffe0} [total free=ffe0]
|
||||
0: After shm_FragmentAlloc(block, 0x010000) == NULL
|
||||
{0x0020,0xffe0} [total free=ffe0]
|
||||
1: After shm_FragmentAlloc(block, 0x003fdc) == 0x00c024
|
||||
{0x0020,0xc000} [total free=c000]
|
||||
2: After shm_FragmentAlloc(block, 0x003ffc) == 0x008024
|
||||
{0x0020,0x8000} [total free=8000]
|
||||
3: After shm_FragmentAlloc(block, 0x003ffc) == 0x004024
|
||||
{0x0020,0x4000} [total free=4000]
|
||||
4: After shm_FragmentAlloc(block, 0x003ffd) == NULL
|
||||
{0x0020,0x4000} [total free=4000]
|
||||
5: After shm_FragmentAlloc(block, 0x003ffc) == 0x000024
|
||||
no free fragments [total free=0000]
|
||||
6: Doing shm_FragmentFree(block, 0x000024)
|
||||
{0x0020,0x4000} [total free=4000]
|
||||
7: After shm_FragmentAlloc(block, 0x001bfc) == 0x002424
|
||||
{0x0020,0x2400} [total free=2400]
|
||||
8: After shm_FragmentAlloc(block, 0x0013fc) == 0x001024
|
||||
{0x0020,0x1000} [total free=1000]
|
||||
9: After shm_FragmentAlloc(block, 0x000ffc) == 0x000024
|
||||
no free fragments [total free=0000]
|
||||
10: Doing shm_FragmentFree(block, 0x000024)
|
||||
{0x0020,0x1000} [total free=1000]
|
||||
11: Doing shm_FragmentFree(block, 0x004024)
|
||||
{0x0020,0x1000} {0x4020,0x4000} [total free=5000]
|
||||
12: Doing shm_FragmentFree(block, 0x00c024)
|
||||
{0x0020,0x1000} {0x4020,0x4000} {0xc020,0x3fe0} [total free=8fe0]
|
||||
13: After shm_FragmentAlloc(block, 0x000ffc) == 0x000024
|
||||
{0x4020,0x4000} {0xc020,0x3fe0} [total free=7fe0]
|
||||
14: Doing shm_FragmentFree(block, 0x000024)
|
||||
{0x0020,0x1000} {0x4020,0x4000} {0xc020,0x3fe0} [total free=8fe0]
|
||||
15: After shm_FragmentAlloc(block, 0x000ffd) == 0x007014
|
||||
{0x0020,0x1000} {0x4020,0x2ff0} {0xc020,0x3fe0} [total free=7fd0]
|
||||
16: Doing shm_FragmentFree(block, 0x008024)
|
||||
{0x0020,0x1000} {0x4020,0x2ff0} {0x8020,0x7fe0} [total free=bfd0]
|
||||
17: Doing shm_FragmentFree(block, 0x001024)
|
||||
{0x0020,0x2400} {0x4020,0x2ff0} {0x8020,0x7fe0} [total free=d3d0]
|
||||
18: Doing shm_FragmentFree(block, 0x002424)
|
||||
{0x0020,0x6ff0} {0x8020,0x7fe0} [total free=efd0]
|
||||
19: Doing shm_FragmentFree(block, 0x007014)
|
||||
{0x0020,0xffe0} [total free=ffe0]
|
283
ipc/bit_array.c
283
ipc/bit_array.c
|
@ -1,283 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: bit_array.c
|
||||
* Purpose : manipulate array of bits
|
||||
* Portability: This is not completely portable, non CISC arcitectures
|
||||
* Might not have atomic Clear/Set/Toggle bit. On those
|
||||
* architectures semaphores should be used.
|
||||
* Big Endian Concerns: This code is big endian compatible,
|
||||
* but the byte order will be different (i.e. bit 0 will be
|
||||
* located in byte 3).
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
/*
|
||||
** uncoment the following line to disable assertions,
|
||||
** this may boost performance by up to 50%
|
||||
*/
|
||||
/* #define NDEBUG */
|
||||
|
||||
#if defined(linux) && !defined(NO_ASM)
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE <= 131328 /* Linux 2.1.x doesn't return values with clear_bit and set_bit */
|
||||
#define HAS_BITOPS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "bit_array.h"
|
||||
#ifdef HAS_BITOPS
|
||||
#include <asm/bitops.h>
|
||||
#else
|
||||
static inline int clear_bit(int bit, int *mem);
|
||||
static inline int set_bit(int bit, int *mem);
|
||||
#endif /* HAS_BITOPS */
|
||||
|
||||
|
||||
#define INT_NR(bit_nr) ((bit_nr) >> INT_LOG2)
|
||||
#define INT_COUNT(bit_count) INT_NR( bit_count + BITS_PER_INT - 1 )
|
||||
#define BIT_IN_INT(bit_nr) ((bit_nr) & (BITS_PER_INT - 1))
|
||||
|
||||
#if !defined(HAS_BITOPS)
|
||||
|
||||
/* first_zero maps bytes value to the index of first zero bit */
|
||||
static char first_zero[256];
|
||||
static int arrays_initialized=0;
|
||||
|
||||
|
||||
/*
|
||||
** initialize static arrays used for bit operations speedup.
|
||||
** Currently initialized: first_zero[256]
|
||||
** set "arrays_initialized" to inidate that arrays where initialized
|
||||
*/
|
||||
|
||||
static void initialize_arrays()
|
||||
{
|
||||
int i;
|
||||
int bit;
|
||||
|
||||
for (i=0 ; i<256 ; i++) {
|
||||
/* find the first zero bit in `i' */
|
||||
for (bit=0 ; bit < BITS_PER_BYTE ; bit++)
|
||||
/* break if the bit is zero */
|
||||
if ( ( (1 << bit) & i )
|
||||
== 0)
|
||||
break;
|
||||
first_zero[i]= bit;
|
||||
}
|
||||
arrays_initialized=1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Find first zero bit in the integer.
|
||||
** Assume there is at least one zero.
|
||||
*/
|
||||
static inline int find_zbit_in_integer(unsigned int integer)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* find the zero bit */
|
||||
for (i=0 ; i < sizeof(int) ; i++, integer>>=8) {
|
||||
int byte= integer & 0xff;
|
||||
|
||||
if (byte != 0xff)
|
||||
return ( first_zero[ byte ]
|
||||
+ (i << BYTE_LOG2) );
|
||||
}
|
||||
assert(0); /* never reached */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return -1 on failure */
|
||||
static inline int find_first_zero_bit(unsigned *array, int bits)
|
||||
{
|
||||
unsigned int integer;
|
||||
int i;
|
||||
int bytes=INT_COUNT(bits);
|
||||
|
||||
if (!arrays_initialized)
|
||||
initialize_arrays();
|
||||
|
||||
for ( i=bytes ; i ; i--, array++) {
|
||||
integer= *array;
|
||||
|
||||
/* test if integer contains a zero bit */
|
||||
if (integer != ~0U)
|
||||
return ( find_zbit_in_integer(integer)
|
||||
+ ((bytes-i) << INT_LOG2) );
|
||||
}
|
||||
|
||||
/* indicate failure */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int test_bit(int pos, unsigned *array)
|
||||
{
|
||||
unsigned int integer;
|
||||
int bit = BIT_IN_INT(pos);
|
||||
|
||||
integer= array[ pos >> INT_LOG2 ];
|
||||
|
||||
return ( (integer & (1 << bit)) != 0
|
||||
? 1
|
||||
: 0 ) ;
|
||||
}
|
||||
|
||||
/*
|
||||
** The following two functions are x86 specific ,
|
||||
** other processors will need porting
|
||||
*/
|
||||
|
||||
/* inputs: bit number and memory address (32 bit) */
|
||||
/* output: Value of the bit before modification */
|
||||
static inline int clear_bit(int bit, int *mem)
|
||||
{
|
||||
int ret;
|
||||
|
||||
__asm__("xor %1,%1\n"
|
||||
"btrl %2,%0\n"
|
||||
"adcl %1,%1\n"
|
||||
:"=m" (*mem), "=&r" (ret)
|
||||
:"r" (bit));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static inline int set_bit(int bit, int *mem)
|
||||
{
|
||||
int ret;
|
||||
__asm__("xor %1,%1\n"
|
||||
"btsl %2,%0\n"
|
||||
"adcl %1,%1\n"
|
||||
:"=m" (*mem), "=&r" (ret)
|
||||
:"r" (bit));
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#endif /* !deined(HAS_BITOPS) */
|
||||
|
||||
|
||||
/* AssembleArray: assemble an array object using existing data */
|
||||
bit_array *AssembleArray(bit_array *new_array, unsigned int *buff, int bits)
|
||||
{
|
||||
assert(new_array!=NULL);
|
||||
assert(buff!=NULL);
|
||||
assert(bits>0);
|
||||
assert((1 << INT_LOG2) == BITS_PER_INT); /* if fails, redefine INT_LOG2 */
|
||||
|
||||
new_array->bits=bits;
|
||||
new_array->array=buff;
|
||||
return new_array;
|
||||
}
|
||||
|
||||
/* ResetArray: reset the bit array to zeros */
|
||||
int ResetArray(bit_array *bits)
|
||||
{
|
||||
int i;
|
||||
int *p;
|
||||
|
||||
assert(bits!=NULL);
|
||||
assert(bits->array!=NULL);
|
||||
|
||||
for(i= INT_COUNT(bits->bits), p=bits->array; i ; p++, i--)
|
||||
*p=0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* VacantBit: find a vacant (zero) bit in the array,
|
||||
* Return: Bit index on success, -1 on failure.
|
||||
*/
|
||||
int VacantBit(bit_array *bits)
|
||||
{
|
||||
int bit;
|
||||
|
||||
assert(bits!=NULL);
|
||||
assert(bits->array!=NULL);
|
||||
|
||||
bit= find_first_zero_bit(bits->array, bits->bits);
|
||||
|
||||
if (bit >= bits->bits) /* failed? */
|
||||
return -1;
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
int SampleBit(bit_array *bits, int i)
|
||||
{
|
||||
assert(bits != NULL);
|
||||
assert(bits->array != NULL);
|
||||
assert(i >= 0 && i < bits->bits);
|
||||
|
||||
return ( test_bit(i,bits->array) != 0
|
||||
? 1
|
||||
: 0
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Use "compare and exchange" mechanism to make sure
|
||||
** that bits are not modified while "integer" value
|
||||
** is calculated.
|
||||
**
|
||||
** This may be the slowest technique, but it is the most portable
|
||||
** (Since most architectures have compare and exchange command)
|
||||
*/
|
||||
int AssignBit(bit_array *bits, int bit_nr, int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(bits != NULL);
|
||||
assert(bits->array != NULL);
|
||||
assert(val==0 || val==1);
|
||||
assert(bit_nr >= 0 && bit_nr < bits->bits);
|
||||
|
||||
if (val==0)
|
||||
ret= clear_bit(BIT_IN_INT(bit_nr), &bits->array[ INT_NR(bit_nr) ]);
|
||||
else
|
||||
ret= set_bit(BIT_IN_INT(bit_nr), &bits->array[ INT_NR(bit_nr) ]);
|
||||
|
||||
return ( (ret!=0) ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a free bit (==0) and make it used (==1).
|
||||
** This operation is guaranteed to resemble an atomic instruction.
|
||||
**
|
||||
** Return: allocated bit index, or -1 on failure.
|
||||
**
|
||||
** There is a crack between locating free bit, and allocating it.
|
||||
** We assign 1 to the bit, test it was not '1' before the assignment.
|
||||
** If it was, restart the seek and assign cycle.
|
||||
**
|
||||
*/
|
||||
|
||||
int AllocateBit(bit_array *bits)
|
||||
{
|
||||
int bit_nr;
|
||||
int orig_bit;
|
||||
|
||||
assert(bits != NULL);
|
||||
assert(bits->array != NULL);
|
||||
|
||||
do {
|
||||
bit_nr= VacantBit(bits);
|
||||
|
||||
if (bit_nr == -1) /* No vacant bit ? */
|
||||
return -1;
|
||||
|
||||
orig_bit = AssignBit(bits, bit_nr, 1);
|
||||
} while (orig_bit != 0); /* it got assigned before we tried */
|
||||
|
||||
return bit_nr;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,93 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "bit_array.h"
|
||||
#define SIZE (8*sizeof(int)*3)
|
||||
|
||||
static bit_array array;
|
||||
static int simple_array[SIZE];
|
||||
static int bits;
|
||||
|
||||
int are_equal()
|
||||
{
|
||||
int i;
|
||||
for (i=0 ; i < SIZE ; i++)
|
||||
if (SampleBit(&array,i) != simple_array[i]){
|
||||
printf("failed bit %d (packed=%d, simple=%d)\n", i,
|
||||
SampleBit(&array,i), simple_array[i]);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_same_vacant()
|
||||
{
|
||||
int vacant;
|
||||
for (vacant =0 ; simple_array[vacant]!=0 ; vacant++)
|
||||
if ( vacant >= SIZE) {
|
||||
vacant=-1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ( VacantBit(&array) == vacant )
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
void assign_both(int bit_nr, int bit_val)
|
||||
{
|
||||
int old_bit= simple_array[bit_nr];
|
||||
|
||||
simple_array[bit_nr]= bit_val;
|
||||
|
||||
bits+=bit_val - old_bit;
|
||||
|
||||
assert(AssignBit(&array, bit_nr, bit_val) == old_bit);
|
||||
assert(are_equal());
|
||||
assert(is_same_vacant());
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
unsigned int integers[SIZE >> 5];
|
||||
int i,j;
|
||||
|
||||
assert( AssembleArray(&array, integers, SIZE)
|
||||
== &array);
|
||||
ResetArray(&array);
|
||||
for (i=0 ; i<SIZE ; i++)
|
||||
simple_array[i]=0;
|
||||
|
||||
for (j=5 ; j ; j--) {
|
||||
printf("\rleft %d\r",j);
|
||||
|
||||
for (i=0 ; VacantBit(&array) != -1 ; i++ ) {
|
||||
if (i % 256 == 0) {
|
||||
printf("left %d ",j);
|
||||
printf("%3d up \r", bits);
|
||||
fflush(stdout);
|
||||
}
|
||||
assign_both(rand() % SIZE,
|
||||
(rand()% SIZE > bits ) ? 0 : 1 );
|
||||
}
|
||||
|
||||
assign_both(rand() % SIZE, 1);
|
||||
|
||||
for (i=0 ; bits ; i++ ) {
|
||||
if (i % 256 == 0) {
|
||||
printf("left %d ",j);
|
||||
printf("%3d down\r", bits);
|
||||
fflush(stdout);
|
||||
}
|
||||
assign_both(rand() % SIZE,
|
||||
(rand()% SIZE <= (SIZE-bits) ) ? 0 : 1 );
|
||||
}
|
||||
|
||||
assign_both(rand() % SIZE, 0);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
962
ipc/dde.tex
962
ipc/dde.tex
|
@ -1,962 +0,0 @@
|
|||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Written by Michael Veksler.
|
||||
%% May be freely redistributed as long as this header is unchanged.
|
||||
%% The file be modified as long as there will be appropriate comment
|
||||
%% containing the Author and a description.
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\documentstyle{article}
|
||||
\renewcommand{\thepage}{}
|
||||
\newcommand{\Wine}{Wine}
|
||||
\newcommand{\WINE}{{\em WINdows Emulator}}
|
||||
\newcommand{\DDE}{{\em Dynamic Data Exchange}}
|
||||
\newcommand{\windoz}{MS-Win\-dows}
|
||||
\newcommand{\fname}[1]{{\tt #1}}
|
||||
\newcommand{\libwine}{\fname{libwine.a}}
|
||||
\title{DDE\footnote{DDE is \DDE\ mechanism provided by \windoz.
|
||||
(Appendix~\protect\ref{sec:DDE}).
|
||||
}
|
||||
\ capability for \Wine\footnote{\Wine\ (\WINE) Runs \windoz\
|
||||
programs under UNIX\@. (Appendix~\protect\ref{sec:Wine}).
|
||||
}
|
||||
}
|
||||
\author{Michael Veksler 11678223}
|
||||
%%\date{Aug 6, 1995}
|
||||
\begin{document}
|
||||
\maketitle
|
||||
\vfill
|
||||
\abstract{\Wine{} provides an environment for running or compiling
|
||||
\windoz{} applications for UNIX clones (such as Linux).
|
||||
\Wine{} is not yet finished and lacks many basic important features.
|
||||
One of the missing features is DDE\@. DDE implementation
|
||||
(especially for \libwine) requires
|
||||
UNIX IPC\footnote{IPC - Inter-Process Communication}. This Project
|
||||
introduces the IPC mechanism into \Wine{}, thus allowing DDE to work
|
||||
between different \Wine{} processes.}
|
||||
|
||||
|
||||
\vfill
|
||||
{\LARGE
|
||||
\begin{itemize}
|
||||
\item[Instructor:] Avner Lottem
|
||||
\item[Done for:] Technion- Israel Institute of Technology,\\
|
||||
Electrical Engineering software lab$^{\mbox{\small\copyright}}$.
|
||||
\end{itemize}
|
||||
}
|
||||
\vfill
|
||||
|
||||
\newpage
|
||||
\renewcommand{\thepage}{\roman{page}}
|
||||
\setcounter{page}{1}
|
||||
\begin{center}
|
||||
{\LARGE Table Of Contents}
|
||||
\end{center}
|
||||
\tableofcontents
|
||||
|
||||
\newpage
|
||||
\renewcommand{\thepage}{\arabic{page}}
|
||||
\setcounter{page}{1}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% end of title
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\section{Introduction}
|
||||
|
||||
\subsection{The project}
|
||||
The purpose of the project is to add DDE capabilities into \Wine.
|
||||
The project enables \Wine{} DDE client-server applications communication.
|
||||
This involved messing around with:
|
||||
\begin{itemize}
|
||||
\item \windoz\ message mechanism.
|
||||
\item \windoz\ memory management.
|
||||
\item \windoz\ task management.
|
||||
\item UNIX messages among \Wine\ processes.
|
||||
\item Exchanging (sharing) UNIX memory.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Problem description}
|
||||
DDE is essentially a communication protocol between \windoz\
|
||||
tasks. Implementing this protocol requires communication among
|
||||
\Wine{} processes and among tasks in one process (each \Wine{} process
|
||||
can contain several tasks). Since \Wine\ already provides
|
||||
communication among internal tasks, this project provides communication
|
||||
between \Wine\ processes.
|
||||
|
||||
Different \Wine\ processes should be able to send \windoz\ messages
|
||||
and to pass \windoz\ memory handles. There also has to be a
|
||||
synchronization mechanism between the processes (\windoz\ applications
|
||||
make assumptions about task switching - and don't use semaphores, or
|
||||
any \windoz\ equivalent).
|
||||
|
||||
Passing messages is a bit tricky. \windoz\ gives a feedback about the
|
||||
success of a message (fails if the window does not exist).
|
||||
In case of SendMessage (see Appendix~\ref{sec:WindozMsg}), the process
|
||||
will have to halt until the message has been processed.
|
||||
In case of PostMessage, things are simpler - only the existence of the
|
||||
recipient has to be checked.
|
||||
|
||||
\windoz\ switches tasks only when certain system calls are used (such
|
||||
as delivering or polling messages). \windoz\ applications rely on this
|
||||
behavior. The problem arises when two applications run on different
|
||||
\Wine\ processes. Since UNIX preempts processes there is no guaranty
|
||||
that the UNIX task will occur at the correct place (in respect of the
|
||||
application).
|
||||
One one hand is the need for compatibility (no UNIX ``current''
|
||||
execution), and on the other hand is performance and stability.
|
||||
|
||||
Another problem is passing handles between \Wine\ processes. \windoz\
|
||||
applications pass atoms\footnote{See appendix~\ref{sec:WhatAreAtoms}},
|
||||
memory handles and window handles. These handles have to be allocated
|
||||
globally (in shared memory) or translated on the fly.
|
||||
|
||||
|
||||
If a process dies from ``unnatural causes'' (e.g.\ kill $-9$). It will
|
||||
leave garbage in memory and other IPC handles will start floating
|
||||
around. There should be some way to collect this garbage.
|
||||
|
||||
\subsection{Solutions}
|
||||
\label{sec:ProjSolutions}
|
||||
\Wine\ uses Sys-V IPC mechanism (See
|
||||
Appendix~\ref{sec:SysV-IPC}). This allows sharing data and passing
|
||||
data between processes (The alternatives are not that good in that
|
||||
respect - see Appendix~\ref{DDE-Alternatives}).
|
||||
The shared data contains only essential information (performance or
|
||||
compatibility). The minimum is kept on shared memory in order to get
|
||||
maximum stability (\windoz\ is known as an unstable system, \Wine\
|
||||
should not imitate \windoz\ in this respect).
|
||||
|
||||
Communications are done by Sys-V messages and UNIX signals (a signal
|
||||
wakes \Wine\ up and the message passes the data). Sys-V messages are
|
||||
used for carrying \windoz\ messages, as well as synchronization
|
||||
information. There are 3 types of messages: sent, posted and ack.
|
||||
Ack message is used for synchronization as well as for returning error
|
||||
codes (In case sending/posting messages fail). When a message is sent,
|
||||
the sending process will block until \Wine's {\em ack}{} message is
|
||||
received.
|
||||
|
||||
Synchronization between \Wine\ processes is divided into 2 categories,
|
||||
in both cases the sender of a message waits for an ack message:
|
||||
\begin{enumerate}
|
||||
\item Sent messages - Wait until the recipient delivers ack message.
|
||||
(When the recipient finished message processing)
|
||||
\item Posted message - The recipient will send ack only during
|
||||
task switching instructions (like PostMessage, GetMessage, Yield,
|
||||
etc.). This way nothing will be done after PostMessage until it is
|
||||
safe.
|
||||
\end{enumerate}
|
||||
|
||||
There is no real \Wine\ server. Every \Wine\ process has to handle
|
||||
it's end. This approach somehow resembles a distributed OS\@. This
|
||||
introduces some problems (Resources might not be freed in case of a
|
||||
crash). On the other hand this ``distributed OS'' approach simplifies
|
||||
installation and use, and improves performance.
|
||||
|
||||
Atoms are stored on shared memory, so every two \Wine\ processes will
|
||||
be in sync in this respect (atom 5 will mean the same on both
|
||||
processes). Shared memory handles are stored on shared memory as
|
||||
well. Shared memory handles are not from the same range as local
|
||||
memory handles (\windoz\ requires an plication to declare about it's
|
||||
shared memory). Since local memory handles have different range they
|
||||
can be allocated locally (without any sync requirements - handle x may
|
||||
appear on 10 different \Wine\ processes and address different data).
|
||||
|
||||
Window handles are allocated locally. This approach gives stability,
|
||||
but window handles are no longer unique (same handle may appear
|
||||
on different processes). This requires on the fly window handle
|
||||
translation. When an \windoz\ message is transfered, \Wine\ will
|
||||
translate any contained window handle. The sender translates the
|
||||
handle to a global (unique) handle, the receiver translates the global
|
||||
handle to the local window handle.
|
||||
|
||||
Collecting the garbage of dead processes is done in two cases:
|
||||
\begin{enumerate}
|
||||
\item When setting up a new \Wine\ process: If no \Wine\ process is
|
||||
running and shared memory is floating around all the IPC stuff is
|
||||
deleted.
|
||||
\item When sending a message: If the recipient is dead it's IPC
|
||||
handles are deleted.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
|
||||
\section{The software}
|
||||
|
||||
\subsection{DDE project structure}
|
||||
Old \Wine\ functions call the new DDE functions whenever needed.
|
||||
(e.g.\ when sending a DDE message, the original SendMessage function calls
|
||||
DDE\_SendMessage). This approach minimizes changes to original \Wine{}
|
||||
code. The new code was written in C. The new capabilities where written
|
||||
as much as possible in OOP\footnote{OOP stands for Object Oriented
|
||||
Programming}{} style.
|
||||
|
||||
Currently DDE uses Sys-V IPC mechanism, for it's operation.
|
||||
|
||||
|
||||
\paragraph{Major OOP objects and files:}
|
||||
|
||||
\begin{table}[h]
|
||||
\begin{center}
|
||||
\leavevmode
|
||||
\begin{tabular}{|l|p{20em}|} \hline
|
||||
Object & Description \\ \hline
|
||||
shm\_block & shared memory block operations. \\
|
||||
shm\_main\_block & (derived from shm\_block.) Things all
|
||||
DDE applications need access to. \\
|
||||
shm\_fragment & Allocation of in-block fragments. \\
|
||||
shm\_semaph & Semaphore operations. \\
|
||||
dde\_proc & Process and DDE message handling. \\
|
||||
dde\_mem & Front end of DDE memory handling
|
||||
(Interface of DDE to outer world). \\
|
||||
dde\_atom & Handling global atoms. \\
|
||||
bit\_array & Array of bits. It has atomic
|
||||
operations, so bits can be used instead
|
||||
of semaphores. \\ \hline
|
||||
\end{tabular}
|
||||
\caption{Objects constructing the DDE extension.}
|
||||
\label{tab:ObjList}
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
Table~\ref{tab:ObjList}{} lists the objects used in the DDE code.
|
||||
The following rules apply to objects:
|
||||
|
||||
\begin{itemize}
|
||||
\item Object names that begin with {\em dde\_$\cdots$}{} are the front
|
||||
end. These objects are working directly with the rest of
|
||||
\Wine. Objects that start with {\em shm\_$\cdots$}{} are the back
|
||||
end. These objects provide services for the rest of the
|
||||
implementation.
|
||||
\item The objects are stored in file with the same name as the object.
|
||||
(e.g.\ dde\_mem is stored in dde\_mem.c and dde\_mem.h).
|
||||
\item For each object there is a test file (that partially tests it).
|
||||
The name of this file is object's name with the word ``test''
|
||||
(e.g.\ dde\_mem object has dde\_mem\_test.c).
|
||||
\item dde.h file has declarations used by dde\_proc and external DDE
|
||||
users.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\subsection{Major algorithms}
|
||||
|
||||
\paragraph{Delivering messages:}
|
||||
\label{sec:DeliveringMessages}
|
||||
\windoz\ messages are passed using Sys-V IPC messages. Processing
|
||||
starts with SendMessage() or PostMessage() \windoz\ system
|
||||
functions:
|
||||
\begin{enumerate}
|
||||
\item SendMessage and PostMessage call the DDE functions (same names
|
||||
prefixed with DDE\_). If the DDE functions pass, don't go any
|
||||
further. If it fails try to process the message in the
|
||||
conventional way.
|
||||
\item The DDE message functions call DDE\_DoMessage, which is
|
||||
the main DDE message processing function. From now on there is no
|
||||
difference between sending and posting messages (from initiator's
|
||||
point of view).
|
||||
\item If the message is not a DDE message or the recipient is not
|
||||
remote, fail and return to original message functions.
|
||||
\item Bring the message to a format suitable for IPC delivery. The
|
||||
format includes:
|
||||
\begin{itemize}
|
||||
\item message type (sent or posted).
|
||||
\item Sender window handle (translated to global window id.)
|
||||
\item Other message ingredients (msg, lParam).
|
||||
\end{itemize}
|
||||
\item Deliver the message to every recipients using
|
||||
DDE\_DoOneMessage. There might be many recipients in case of a
|
||||
broadcast (when the ``recipient'' window is -1).
|
||||
\item DDE\_DoOneMessage delivers the message and waits for response.
|
||||
Fail if timeout is reached.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{Receiving messages:}
|
||||
Messages from other \Wine\ processes are received. Processing start
|
||||
from DDE\_GetRemoteMessage():
|
||||
\begin{enumerate}
|
||||
\item DDE\_GetRemoteMessage is called whenever wine is going to wait
|
||||
or receive X-windows message. If wine is sleeping (waiting for X),
|
||||
it will waken by a SIGUSR2 signal. In this case the handler will
|
||||
siglongjmp() out of the select() function.
|
||||
\item If no remote message is pending in the message queue - exit.
|
||||
(Sys-V message queue holds the remote messages until they are
|
||||
read).
|
||||
\item If the message is broadcasted, goto stage~\ref{BroadcastAlgo}.
|
||||
\item Translate the global window handle of the recipient to local
|
||||
handle.
|
||||
\item Do this if the message was sent. Send the message to the
|
||||
recipient window. Reply with ack to the sender. (Reply with the
|
||||
return code of the SendMessage function). {\em \bf Return}\ with
|
||||
positive return value.
|
||||
\item Post the message.
|
||||
\item If PostMessage fails - send failing ack message.
|
||||
\item If PostMessage passes, add the message data into a FIFO\@. This
|
||||
FIFO contains all messages that have to be acknowledged (who's
|
||||
sender is waiting for ack). Whenever internal task switch occurs,
|
||||
a item from this FIFO is popped out and an ack message is sent.
|
||||
\item \label{BroadcastAlgo} Iterate through the local windows and
|
||||
deliver the message to every one of them.
|
||||
\item Send a positive ack message.
|
||||
\end{enumerate}
|
||||
|
||||
\paragraph{Allocating memory:}
|
||||
Shared memory allocation is divided into two categories: internal wine
|
||||
and \windoz\ application. The memory used by \windoz\ applications is
|
||||
allocated in two phases - allocate internal wine, and then prepare all
|
||||
the stuff needed by \windoz.
|
||||
|
||||
The internal allocation (DDE\_malloc):
|
||||
\begin{enumerate}
|
||||
\item Try to allocate big enough memory fragment in allocated shared
|
||||
memory.
|
||||
\item If fragment could not be allocated, allocate a new shared
|
||||
memory block. Allocate the memory fragment.
|
||||
\item Allocate a shared memory handle (call the dde\_alloc\_handle()
|
||||
function). The allocated handle is from a range used only by
|
||||
shared memory handles. This allocation function is used only for
|
||||
shared DDE memory.
|
||||
\item Write the handle in the data structure. Also write the
|
||||
relative location of this handle (Pointer location is useless since
|
||||
it differs from one \Wine\ process to another).
|
||||
This information is written in the main shared memory block.
|
||||
\end{enumerate}
|
||||
When a fragment is allocated, it is removed from the list of free
|
||||
memory.
|
||||
|
||||
Allocation for \windoz\ applications (this is done in GlobalAlloc):
|
||||
\begin{enumerate}
|
||||
\item Do the internal allocation (instead of malloc in the local
|
||||
allocation).
|
||||
\item Everything else is the same as with local memory. A segment is
|
||||
allocated for the new memory, and it's data is kept in the data
|
||||
structure.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{Atoms:}
|
||||
Atoms have pretty simple algorithms operating on them. Since atoms are
|
||||
supposed to give a unique number for each string - atoms are
|
||||
implemented as hash tables. The id of the atom is the entry in the
|
||||
hash table. The disadvantage of this approach is that there is a limit
|
||||
on the number of atoms, the big advantages are simplicity and
|
||||
speed.
|
||||
|
||||
Since implementing a hash tables is strait forward, I will not
|
||||
discuss it in depth. This hash table has two hash functions - primary
|
||||
and secondary. When searching for an item (for deletion, addition, or
|
||||
a seek) the following steps are taken:
|
||||
\begin{enumerate}
|
||||
\item The primary hash function returns the first entry.
|
||||
\item \label{AtomHashLoop}
|
||||
If the entry is empty or no unchecked entries {\bf \em fail}.
|
||||
\item If the entry is correct, {\bf \em success!!!}.
|
||||
\item Add the result of secondary hash function to the item index.
|
||||
\item Goto step~\ref{AtomHashLoop}.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\subsection{Major data structures}
|
||||
There are two kinds of data structure, local and shared. The local
|
||||
data structures are strait forward and will not be discussed in
|
||||
detail.
|
||||
|
||||
The shared data structures have four level hierarchy. Each level
|
||||
depend on the other and can not exist without it. Most structures have
|
||||
much less than four levels.
|
||||
|
||||
\paragraph{Level one data structure - the main block:}
|
||||
This data structure is part of the shm\_main\_block object. It
|
||||
contains essential information needed by all \Wine\ processes. :
|
||||
\begin{enumerate}
|
||||
\item Semaphore identifier used for locking the data structure.
|
||||
\item A table containing information about \Wine\ processes.
|
||||
\item Relative pointers to atoms.
|
||||
\item A table containing window information.
|
||||
\item Tables with global memory handles.
|
||||
\end{enumerate}
|
||||
|
||||
The first level is allocated and initialized when \Wine\ calls a DDE
|
||||
function the first time. DDE functions are: global memory allocation,
|
||||
global atoms or DDE messages.
|
||||
|
||||
The first level data structure contains the second level data
|
||||
structure.
|
||||
|
||||
|
||||
\paragraph{Atom element (level two):}
|
||||
Each element is allocated dynamically (the size of the string is
|
||||
unknown). The allocation is gained from the shm\_fragment object (each
|
||||
atom is an instance of that object). Each atom contains:
|
||||
\begin{enumerate}
|
||||
\item Count of links. Each allocation of the atom establishes a new
|
||||
link and each deallocation deletes one link (until all links are
|
||||
removed).
|
||||
\item The string of the atom. The string ends with a null character.
|
||||
\end{enumerate}
|
||||
|
||||
\paragraph{Wine processes data (level two):}
|
||||
(part of the dde\_proc object) :
|
||||
\begin{enumerate}
|
||||
\item pid (\Wine\ process id).
|
||||
\item Message queue id.
|
||||
\item Semaphore used for locking process's data structure.
|
||||
\item Sys-V shared memory id of the first block in the chain of
|
||||
process's blocks.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{Window information (level two):}
|
||||
This is part of the dde\_proc object. The index of array entry
|
||||
specifies the window handle. Each entry contains:
|
||||
\begin{enumerate}
|
||||
\item The process index of the \Wine\ process running this window.
|
||||
\item The local window handle of this window (remember that each DDE
|
||||
window has different global and local handles).
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{Global memory handles (Level two):}
|
||||
(part of bit\_array and dde\_mem objects):
|
||||
\begin{enumerate}
|
||||
\item A bit array contains '1' in every entry that refers to
|
||||
used global memory handle.
|
||||
\item Array of handles contains handle data. Each entry refers
|
||||
to a global memory handle (The index is a function of the
|
||||
handle). The data includes: 1) Sys-V shared memory handles 2)
|
||||
offset into the memory block.
|
||||
\end{enumerate}
|
||||
|
||||
\paragraph{Block allocation data structure (Level three):}
|
||||
This data structure a part of the shm\_block object. It is local to
|
||||
each shared memory block, so every memory block has it's own data
|
||||
structure. The shared memory blocks are organized in chains grouped by
|
||||
processes (each DDE process has one and only one chain):
|
||||
\begin{enumerate}
|
||||
\item The id of the next shared memory block in the chain.
|
||||
\item The size of the block.
|
||||
\item Number of free bytes in the block.
|
||||
\item The index of the owner process.
|
||||
\item The first item in the free list (a linked list of free items).
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{Fragment allocation (Level four):}
|
||||
This data is a part of the shm\_fragment object. It is a part of the shared
|
||||
memory allocation mechanism. Every fragment is a part of shared memory
|
||||
block. Each fragments contain:
|
||||
\begin{enumerate}
|
||||
\item The size of the fragment (for allocating and freeing this
|
||||
fragment).
|
||||
\item A union that contains one of:
|
||||
\begin{itemize}
|
||||
\item data - if it is allocated.
|
||||
\item offset of the next free fragment (if it is a member of the
|
||||
free list).
|
||||
\end{itemize}
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{Local reference to global memory:}
|
||||
Global memory blocks are referenced from local memory. This is used
|
||||
for storing pointers that refer to the memory block. It contains:
|
||||
\begin{enumerate}
|
||||
\item next item in the list.
|
||||
\item shared memory block id.
|
||||
\item Owner process.
|
||||
\item Pointer to the data.
|
||||
\end{enumerate}
|
||||
|
||||
\paragraph{Local mirroring of global handles:}
|
||||
Global handles require local segments handles (when they are locked).
|
||||
These segments have to be deleted when the handle is freed.
|
||||
There is a minimal mirroring of handles in the original tables of
|
||||
wine, this information is stored the way local handles have always
|
||||
been stored.
|
||||
|
||||
\subsection{Interface and IO}
|
||||
The DDE section of wine has several interfaces to different entities:
|
||||
\begin{enumerate}
|
||||
\item User debug information.
|
||||
\item Interface between several \Wine\ processes.
|
||||
\item Interface to the `old' code of \Wine.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\paragraph{User debug information:}
|
||||
The debug information is written according to \Wine's
|
||||
conventions. This means that dprintf\_$\cdots$ macros are used to
|
||||
print the debug information. (These macros allow runtime or compile time
|
||||
disabling of a any sort of debug information).
|
||||
|
||||
In some cases the dprintf\_$\cdots$ macros are not powerful enough. In
|
||||
those cases the following construct is used instead:
|
||||
\begin{verbatim}
|
||||
if (debugging_dde) {
|
||||
/* print whatever is needed for dde debugging */
|
||||
}
|
||||
\end{verbatim}
|
||||
|
||||
|
||||
\paragraph{Interface between \Wine\ processes:}
|
||||
The interface between processes includes:
|
||||
\begin{itemize}
|
||||
\item Message delivery (see Section~\ref{sec:DeliveringMessages}).
|
||||
\item Sharing \windoz\ constructs (atoms, memory, window
|
||||
handles).
|
||||
\item Synchronization among different \Wine\ processes so it will
|
||||
appear to a \windoz\ as if there is only one process.
|
||||
\end{itemize}
|
||||
|
||||
\paragraph{Interface to the rest of \Wine:}
|
||||
Most of the interface has been minimized to an additional few lines in
|
||||
the original functions. The hook for DDE are in the following
|
||||
locations:
|
||||
\begin{itemize}
|
||||
\item Global atoms functions - these are called directly from
|
||||
\windoz\ applications.
|
||||
\item Message delivery - a hook calls the DDE functions from the
|
||||
original message delivery functions.
|
||||
\item Initialization - Every PeekMessage() call a DDE function to
|
||||
check if the current window has DDE management (by sending
|
||||
a dummy WM\_DDE\_INITIATE message).
|
||||
\item Waiting on messages - The original code for waiting on X
|
||||
messages was enhanced to wait on DDE messages as well. This
|
||||
includes aborting sleeps when SIGUSR2 signal is sent. The actual
|
||||
test for DDE messages is done in the DDE code.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Major functions and their role}
|
||||
\paragraph{Message delivery functions:}
|
||||
\begin{itemize}
|
||||
\item DDE\_SendMessage and DDE\_PostMessage - hooks used by original
|
||||
\Wine's SendMessage and PostMessage. They return true if the
|
||||
message could be successfully sent to a remote \Wine\ process.
|
||||
Only in this case SendMessage and PostMessage stop further
|
||||
processing.
|
||||
\item DDE\_DoMessage - Try to post or send a message to another
|
||||
\Wine\ process. This function can do broadcasting as well as
|
||||
sending. (A message is `broadcasted' if it is delivered to every
|
||||
window on the system).
|
||||
\item DDE\_DoOneMessage - Deliver a message to a given wine
|
||||
{\bf process}.
|
||||
\item dde\_proc\_send\_ack - Send ack message to \Wine\
|
||||
process. The input window handle is used for detecting the
|
||||
process.
|
||||
\item get\_ack - wait for ack or timeout.
|
||||
\end{itemize}
|
||||
|
||||
\paragraph{Memory allocation functions:}
|
||||
\begin{itemize}
|
||||
\item DDE\_malloc - Allocate a shared memory fragment. This function
|
||||
also allocates global memory handle.
|
||||
\item DDE\_GlobalFree - Free a shared memory fragment according to
|
||||
it's handle. And free the handle.
|
||||
\item DDE\_SyncHandle - synchronize the global handles mirrored
|
||||
locally. Delete any outdated handles, or remap reallocated
|
||||
handles.
|
||||
\item DDE\_mem\_init - initialize or attach the main shared memory
|
||||
block, and relevant local data structures.
|
||||
\item shm\_delete\_all - delete all IPC stuff related to this
|
||||
process. Kill the main memory block if appropriate.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\section{Software testing}
|
||||
The testing of this module was a substantial part of the
|
||||
development. Testing revealed bugs just as they where created.
|
||||
|
||||
\subsection{Testing methods utilized}
|
||||
\label{sec:TestMethod}
|
||||
Two major testing methods where used:
|
||||
\begin{enumerate}
|
||||
\item Running DDE \windoz\ applications. The applications used are
|
||||
DDE demonstration applications. This applications seem to show
|
||||
precisely what DDE is all about. The applications used where:
|
||||
ddepop1.exe (server), showpop1.exe (client). These
|
||||
applications come with \windoz\ programming manual by Charles
|
||||
Petzold \cite{bib:WinManual}.
|
||||
\item Running standalone DDE objects linked with a test
|
||||
object. Every object was tested this way. Some objects had to be
|
||||
linked with a stub that emulated \Wine's functions. The stub is
|
||||
pretty poor with dde\_mem object since this object requires hard
|
||||
to emulate functionalities (things like allocating a segment).
|
||||
|
||||
For some objects the coverage seems to be perfect (100\%). For
|
||||
some object the coverage is poor. The objects with massive stub
|
||||
usage suffer from this (especially dde\_mem).
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{input for testing}
|
||||
Most of the testing described in section~\ref{sec:TestMethod} has no
|
||||
input. Most of the testing knowledge is in the test objects
|
||||
themselves. There are two exceptions to this rule:
|
||||
\begin{enumerate}
|
||||
\item The \windoz\ applications described in
|
||||
section~\ref{sec:TestMethod} - which are essentially an input to
|
||||
the software. These applications can't be distributed since it
|
||||
will be a \copyright{} violation.
|
||||
\item A trace file which is used for comparing test's output with
|
||||
the expected trace result. This trace file is used for
|
||||
shm\_fragment validation. This file is implementation Dependants
|
||||
and should be updated whenever the object is rewritten. When
|
||||
updating the trace, it should be inspected manually before it is
|
||||
released.
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{usage instructions}
|
||||
|
||||
\paragraph{Running the DDE applications:}
|
||||
To test the DDE applications one must run:
|
||||
\begin{verbatim}
|
||||
prompt> wine /full-path/ddepop1.exe > /dev/null &
|
||||
\end{verbatim}
|
||||
Wait for the server window to pop up (A tiny window should appear at
|
||||
the lower left corner of your screen). Then activate the client by
|
||||
typing:
|
||||
\begin{verbatim}
|
||||
prompt> wine /full-path/showpop1.exe > /dev/null
|
||||
\end{verbatim}
|
||||
The server updates ``USA population'' data every 5 seconds. The client
|
||||
asks for a hot link (i.e.\ it sends a WM\_DDE\_ADVISE on every
|
||||
item). From that point on the server updates the client of any
|
||||
population change.
|
||||
|
||||
|
||||
\paragraph{Running the object tests:}
|
||||
The {\em run\_tests}{} script is provided to automate the object
|
||||
tests. Every object test is run and the script checks for results. At
|
||||
the end the script prints a nice summary table.
|
||||
|
||||
Invoking tests outside the {\em run\_tests}{} script is not as simple
|
||||
as it may look. Every test has differently looking output.
|
||||
\begin{itemize}
|
||||
\item dde\_atom\_test - The executable is run. It passes if no
|
||||
errors are printed at the bottom.
|
||||
\item dde\_mem\_test - The same as for dde\_atom\_test.
|
||||
\item shm\_semaph\_test - The same as for dde\_atom\_test.
|
||||
\item bit\_array\_test - The same as for dde\_atom\_test.
|
||||
\item shm\_fragment\_test - After the executable is run, the output
|
||||
has to be compared with TEST\_FRAGMENT.std
|
||||
\item dde\_proc\_test - This actually runs as a client and as a
|
||||
server. To activate the server one should write:
|
||||
\begin{verbatim}
|
||||
prompt> dde_proc_test 1 > server.log
|
||||
\end{verbatim}
|
||||
Then the client has to be activated within five seconds by
|
||||
writing:
|
||||
\begin{verbatim}
|
||||
prompt> dde_proc_test > client.log
|
||||
\end{verbatim}
|
||||
The log files should contain:
|
||||
\begin{description}
|
||||
\item[server.log:] Should contain traces of received
|
||||
WM\_DDE\_INITIATE message, and ack sent for it. (Pretty much
|
||||
the same what {\em run\_tests}{} does).
|
||||
\item[client.log:] Should contain traces of sending
|
||||
WM\_DDE\_INITIATE and then receiving an ack message.
|
||||
\end{description}
|
||||
No `timeout' traces should be present in the log files.
|
||||
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\section{Summary}
|
||||
\subsection{Software limitations}
|
||||
\begin{itemize}
|
||||
\item Only most fundamental memory management functions where
|
||||
written. This means that the following functions will not work:
|
||||
\begin{itemize}
|
||||
\item GlobalReAlloc
|
||||
\item GlobalSize - two line fix.
|
||||
\item GlobalFlags - two line fix.
|
||||
\item LockSegment - two line fix.
|
||||
\item UnlockSegment - two line fix.
|
||||
\item GlobalFreeAll - will not free DDE memory.
|
||||
\item GlobalPageLock - two line fix.
|
||||
\item GlobalPageUnlock - two line fix.
|
||||
\item GlobalFix - two line fix.
|
||||
\item GlobalUnfix - two line fix.
|
||||
\end{itemize}
|
||||
\item Non DDE messages to remote windows will not work. This is
|
||||
currently only a sanity check, it may be removed in the future.
|
||||
\item Message sending has a timeout limit. This is incompatible with
|
||||
\windoz. This limit increases stability of a singe \Wine\ process.
|
||||
\item Synchronization mechanism between wine processes is not
|
||||
perfect. It was not proved theoretically. I never saw it fail.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Concluding Remarks}
|
||||
This extension to \Wine{} enables further development of \windoz\ IPC
|
||||
support. Things like OLE can be extended using these features -
|
||||
although some modifications might be inevitable.
|
||||
|
||||
The most important contribution of this project is the support for
|
||||
\libwine{}. Any \windoz\ application compiled under UNIX and linked to
|
||||
\libwine{} will need the IPC mechanism provided by this project.
|
||||
|
||||
During the progress of the project it got a bit outdated. During that
|
||||
time \Wine{} started to handle tasks internally, so the external IPC
|
||||
was no longer needed for the emulator configuration of
|
||||
wine. Nevertheless the support of IPC for \libwine will never
|
||||
be obsolete.
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%
|
||||
%% Appendixes
|
||||
%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\clearpage
|
||||
\appendix
|
||||
|
||||
\section{\Wine}
|
||||
\label{sec:Wine}
|
||||
\Wine\ stand for {\em WINdows Emulator}. It is an X-windows - UNIX
|
||||
application (Linux and FreeBSD). \Wine\ emulates \windoz\ environment
|
||||
for the \windoz\ applications. It has two uses:
|
||||
\begin{enumerate}
|
||||
\item Run \windoz\ binary code (compiled under \windoz).
|
||||
\item To be used as a toolkit for compiling \windoz\ applications
|
||||
under UNIX. (The \libwine\ library is provided)
|
||||
\end{enumerate}
|
||||
For emulation, only Intel-Architecture CPU (386, 486, Pentium) are
|
||||
supported. \libwine\ Toolkit should run on any platforms, but it was not
|
||||
tested. \Wine\ is still in alpha state, and does not support all \windoz\
|
||||
capabilities yet.
|
||||
|
||||
|
||||
\section{Messages under \windoz}
|
||||
\label{sec:WindozMsg}
|
||||
Messages are the means of communication to a user window (from another
|
||||
window or from \windoz\ itself).
|
||||
Messages can be delivered in two ways:
|
||||
\begin{itemize}
|
||||
\item Messages can be sent. The sent message enters the window handler
|
||||
asynchronously (The handler may be reentered many times).
|
||||
A window sending the message waits for the recipient to process
|
||||
the message and return an exit code.
|
||||
\item Messages can be posted. The posted message enters a queue, and
|
||||
it's up to the recipient to process it. If the recipient ignores
|
||||
it, the message might be eventually sent to the recipient from
|
||||
WinMain handler.
|
||||
\end{itemize}
|
||||
|
||||
\section{DDE}
|
||||
\label{sec:DDE}
|
||||
DDE stands for \DDE\@. DDE is an \windoz\
|
||||
protocol used for data exchange between applications.
|
||||
It has synchronous and asynchronous operation modes.
|
||||
DDE mechanism is based on messages, atoms
|
||||
(Section~\ref{sec:WhatAreAtoms}) and shared memory.
|
||||
|
||||
DDE applications are divided roughly into two classes: servers AND
|
||||
clients. Clients have the initiative, they try to connect to the
|
||||
server. Servers only maintain an existing DDE link.
|
||||
|
||||
|
||||
\subsection{DDE Messages}
|
||||
\paragraph{DDE message format}:
|
||||
|
||||
\label{sec:DDEMessageFormat}
|
||||
|
||||
DDE messages contain the following fields.
|
||||
\begin{description}
|
||||
\item[wnd] The recipient of this message ($-1$ if every window
|
||||
should receive).
|
||||
\item[msg] The ID of the message.
|
||||
\item[wParam] Who sent this message.
|
||||
\item[lParam] 32 bit parameter, divided into two 16 bit words.
|
||||
Usually, the lower 16 bits contain a memory handle. The handle
|
||||
points to some extra DATA about this message.
|
||||
|
||||
\end{description}
|
||||
|
||||
\paragraph{Available messages:}
|
||||
\begin{description}
|
||||
\item[WM\_DDE\_INITIATE]
|
||||
This message is sent by the client to the whole ``world''.
|
||||
{\em lParam}{} contains the two atoms used for passing the topic and
|
||||
application information.
|
||||
|
||||
Every server application checks if this message refers to it
|
||||
(According to the Application and the Topic atoms).
|
||||
|
||||
|
||||
\item[WM\_DDE\_ACK]
|
||||
This is a generic response to various events
|
||||
(one of them is WM\_DDE\_INITIATE).
|
||||
The low part of {\em lParam}{} passes extra information, such as
|
||||
success/failure.
|
||||
|
||||
\item[WM\_DDE\_TERMINATE]
|
||||
Kill the DDE link.
|
||||
|
||||
|
||||
\item[WM\_DDE\_ADVISE]
|
||||
Tell the server to inform the client of any updates.
|
||||
|
||||
|
||||
\item[WM\_DDE\_UNADVISE]
|
||||
Stop the WM\_DDE\_UNADVISE mode.
|
||||
|
||||
|
||||
\item[WM\_DDE\_DATA]
|
||||
Transfer data handle (note that the data handle must be globally allocated
|
||||
with the GMEM\_DDESHARE flag).
|
||||
|
||||
\item[WM\_DDE\_REQUEST]
|
||||
Request a data item from the server. This message is used in a
|
||||
cold link. {\em lParam}{} contains an atom describing the desired
|
||||
item, and a WORD describing the wanted data format.
|
||||
\item[WM\_DDE\_POKE]
|
||||
The client gives the server new data.
|
||||
\item[WM\_DDE\_EXECUTE]
|
||||
The client asks the server to execute a procedure (according to a
|
||||
command string).
|
||||
|
||||
\end{description}
|
||||
|
||||
\subsection{Atoms}
|
||||
\label{sec:WhatAreAtoms}
|
||||
Atoms are represented by numbers. Every atom maps uniquely to a string
|
||||
(i.e.\ two atoms can't map to the same string).
|
||||
There are two types of atoms - local and global. The local are visible
|
||||
only to the application that created them. Global atoms are seen by all
|
||||
\windoz\ applications.
|
||||
|
||||
Functions:
|
||||
\begin{description}
|
||||
\item[AddAtom(str)] create a local atom that maps to ``str''.
|
||||
If it exists, increment the allocation-counter,
|
||||
and return the atom.
|
||||
\item[DeleteAtom(atom)] decrement the allocation-counter. When it
|
||||
reaches zero the atom, will be deleted.
|
||||
\item[FindAtom(str)] Find the atom that maps to this string (if exist).
|
||||
|
||||
\item[GetAtomName(\ldots)] Find the string that this atom points to.
|
||||
\end{description}
|
||||
|
||||
NOTE:
|
||||
\begin{description}
|
||||
\item[Global atoms] Add ``Global'' before the listed names to get Global
|
||||
atom functions.
|
||||
\item[Case sensitivity] All atom operations ignore case.
|
||||
\end{description}
|
||||
|
||||
|
||||
\section{DDE under \Wine\ - Alternatives}
|
||||
\label{DDE-Alternatives}
|
||||
There where three possible approaches for this project. I have chosen
|
||||
System-V IPC, but it can be easily changed.
|
||||
|
||||
\subsection{IPC - Inter-Process Communication}
|
||||
\label{sec:SysV-IPC}
|
||||
|
||||
The IPC mechanism was originated from System-V UNIX\@. This mechanism
|
||||
allows complicate communication between two process (on the same machine).
|
||||
IPC includes the following capabilities:
|
||||
\begin{enumerate}
|
||||
\item Shared Memory (shm) - can be shared among few processes and users.
|
||||
\item Semaphores (These semaphores allow grouped operations - i.e.\
|
||||
operations gathered as an atomic operation).
|
||||
\item Message Queues (allow few messages at a time).
|
||||
\end{enumerate}
|
||||
|
||||
The advantages of IPC over the other variants are:
|
||||
\begin{itemize}
|
||||
\item Memory access speed is good. (after the memory was attached).
|
||||
\item Easy to block critical code (don't enter critical code from two
|
||||
processes at the same time)
|
||||
\end{itemize}
|
||||
Disadvantages of IPC:
|
||||
\begin{itemize}
|
||||
\item Impossible to communicate to other machine.
|
||||
\item Requires complex mechanism to wait both for X and IPC messages.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{X-Windows Communication mechanisms}
|
||||
The advantages of X-Windows Communication over the other variants are:
|
||||
\begin{itemize}
|
||||
\item Possible to communicate to other machines.
|
||||
\item No need for additional mechanism that will wait both for X and DDE\@.
|
||||
\item Many of the required capabilities are already there.
|
||||
\end{itemize}
|
||||
Disadvantages of X-windows communication:
|
||||
\begin{itemize}
|
||||
\item {\em Slow}\ and complex memory access.
|
||||
\item May be hard to avoid two applications from entering critical
|
||||
code at the same time.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{BSD Sockets}
|
||||
The advantages of Sockets are:
|
||||
\begin{itemize}
|
||||
\item Possible to communicate to other machines.
|
||||
\item No need for complex mechanism that will wait both for X and
|
||||
DDE\@. (can use one ``select'' on X-windows socket in concert with
|
||||
the DDE socket).
|
||||
\end{itemize}
|
||||
Disadvantages of socket are:
|
||||
\begin{itemize}
|
||||
\item {\em Slow}{} and complex memory access. (note that mmap on a
|
||||
descriptor does not work for Linux 0.99).
|
||||
\item May be hard to avoid two applications from entering critical
|
||||
code at the same time.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Why was it written in C? Pros and cons.}
|
||||
Advantages of C:
|
||||
|
||||
\begin{itemize}
|
||||
\item History- \Wine{} is already written in C.
|
||||
\item Portability- not all systems have C++.
|
||||
\item Speed- C++ sometimes makes it very difficult to write fast code.
|
||||
\item Maintenance- More programmers know C than C++.
|
||||
\end{itemize}
|
||||
|
||||
Advantages of C++:
|
||||
|
||||
\begin{itemize}
|
||||
\item smaller code - Templates allow Smaller and more readable code.
|
||||
\item Maintenance - It is easier to add and modify code in C++ (than in C).
|
||||
\item Verification - Functionality can be verified and debugged for
|
||||
individual objects.
|
||||
\end{itemize}
|
||||
|
||||
I have decided to use C. Most of C++ advantages can be acquired
|
||||
by using OOP methodologies. OOP methods that require inline functions
|
||||
can't be used (without performance impact). GCC supports inline functions,
|
||||
but this solution is not compatible.
|
||||
|
||||
|
||||
|
||||
\section{\Wine\ basics}
|
||||
\subsection{How it runs}
|
||||
\Wine\ works in the following order:
|
||||
\begin{enumerate}
|
||||
\item \Wine\ loads the \windoz\ application into it's memory.
|
||||
(in the process it, links
|
||||
DLL's\footnote{DLL (Dynamically Linked Library) is linked on load-time.}
|
||||
creates 16-bit segments).
|
||||
\item \windoz\ code is executed.
|
||||
\end{enumerate}
|
||||
|
||||
\section{bibliography}
|
||||
\begin{thebibliography}{99}
|
||||
\bibitem{bib:WinManual} Charles Petzold {\em Programming
|
||||
Windows~3.1}{} - 1992. \\
|
||||
On cover: {\em the Microsoft guide to writing applications for
|
||||
windows~3.1}. \\
|
||||
Published by {\em Microsoft press}.
|
||||
\end{thebibliography}
|
||||
|
||||
|
||||
\newpage
|
||||
|
||||
|
||||
\end{document}
|
287
ipc/dde_atom.c
287
ipc/dde_atom.c
|
@ -1,287 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_atom.c
|
||||
* Purpose : atom functionality for DDE
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "dde_atom.h"
|
||||
#include "shm_main_blk.h"
|
||||
#include "shm_fragment.h"
|
||||
#include "ldt.h"
|
||||
#include "debugtools.h"
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(atom)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD count;
|
||||
BYTE str[1];
|
||||
} AtomData, *AtomData_ptr;
|
||||
|
||||
#define EMPTY 0 /* empty hash entry */
|
||||
#define DELETED -1 /* deleted hash entry */
|
||||
#define MIN_STR_ATOM 0xfc00
|
||||
|
||||
/* OFS2AtomData_ptr: extract AtomData_ptr from ofs */
|
||||
#define OFS2AtomData_ptr(ofs) ((AtomData*)((int)&main_block->block+(ofs)))
|
||||
|
||||
/* OFS2AtomStr: find the string of the atom */
|
||||
#define OFS2AtomStr(ofs) (OFS2AtomData_ptr(atom_ofs)->str)
|
||||
|
||||
/* offset of an atom according to index */
|
||||
#define ATOM_OFS(idx) (main_block->atoms[idx])
|
||||
|
||||
/* rot_left: rotate (with wrap-around) */
|
||||
static inline int rot_left(unsigned var,int count)
|
||||
{
|
||||
return (var<<count) | (var>> (sizeof(var)-count));
|
||||
}
|
||||
/* find the entry in the atom table for this string */
|
||||
static int FindHash(LPCSTR str) /* ignore str case */
|
||||
{
|
||||
int i,j;
|
||||
unsigned hash1,hash2;
|
||||
int deleted=-1; /* hash for deleted entry */
|
||||
int atom_ofs;
|
||||
|
||||
/* get basic hash parameters */
|
||||
for (i= hash1= hash2= 0; str[i] ; i++) {
|
||||
hash1= rot_left(hash1,5) ^ toupper(str[i]);
|
||||
hash2= rot_left(hash2,4) ^ toupper(str[i]);
|
||||
}
|
||||
|
||||
hash1%= DDE_ATOMS;
|
||||
atom_ofs=ATOM_OFS(hash1);
|
||||
switch (atom_ofs) {
|
||||
case EMPTY: /* empty atom entry */
|
||||
return hash1;
|
||||
case DELETED: /* deleted atom entry */
|
||||
deleted=hash1;
|
||||
break;
|
||||
default : /* non empty atom entry */
|
||||
if (lstrcmpi16( OFS2AtomStr(atom_ofs) , str) == 0)
|
||||
return hash1; /* found string in atom table */
|
||||
}
|
||||
hash2%= DDE_ATOMS-1 ; /* hash2=0..(DDE_ATOMS-2) */
|
||||
hash2++; /* hash2=1..(DDE_ATOMS-1) */
|
||||
|
||||
/* make jumps in the hash table by hash2 steps */
|
||||
for (i=hash1+hash2 ; ; i+=hash2) {
|
||||
/* i wraps around into j */
|
||||
j=i-DDE_ATOMS;
|
||||
if (j >= 0)
|
||||
i=j; /* i wraps around */
|
||||
|
||||
if (i==hash1)
|
||||
/* here if covered all hash locations, and got back to beginning */
|
||||
return deleted; /* return first empty entry - if any */
|
||||
atom_ofs=ATOM_OFS(i);
|
||||
switch (atom_ofs) {
|
||||
case EMPTY: /* empty atom entry */
|
||||
return i;
|
||||
case DELETED: /* deleted atom entry */
|
||||
if (deleted < 0)
|
||||
/* consider only the first deleted entry */
|
||||
deleted= i;
|
||||
break;
|
||||
default : /* nonempty atom entry */
|
||||
if (lstrcmpi16( OFS2AtomStr(atom_ofs) , str) == 0)
|
||||
return i; /* found string in atom table */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ATOM_GlobalInit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i < DDE_ATOMS ; i++)
|
||||
ATOM_OFS(i)=EMPTY;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DDE_GlobalAddAtom
|
||||
*/
|
||||
|
||||
/* important! don't forget to unlock semaphores before return */
|
||||
ATOM DDE_GlobalAddAtom( SEGPTR name )
|
||||
{
|
||||
int atom_idx;
|
||||
int atom_ofs;
|
||||
AtomData_ptr ptr;
|
||||
ATOM atom;
|
||||
char *str;
|
||||
|
||||
/* First check for integer atom */
|
||||
|
||||
if (!HIWORD(name)) return (ATOM)LOWORD(name);
|
||||
|
||||
str = (char *)PTR_SEG_TO_LIN( name );
|
||||
if (str[0] == '#')
|
||||
{
|
||||
ATOM atom= (ATOM) atoi(&str[1]);
|
||||
return (atom<MIN_STR_ATOM) ? atom : 0;
|
||||
}
|
||||
|
||||
TRACE("(\"%s\")\n",str);
|
||||
|
||||
DDE_IPC_init(); /* will initialize only if needed */
|
||||
|
||||
shm_write_wait(main_block->sem);
|
||||
|
||||
atom_idx=FindHash(str);
|
||||
atom=(ATOM)0;
|
||||
|
||||
/* use "return" only at the end so semaphore handling is done only once */
|
||||
if (atom_idx>=0) {
|
||||
/* unless table full and item not found */
|
||||
switch (atom_ofs= ATOM_OFS(atom_idx)) {
|
||||
case DELETED:
|
||||
case EMPTY: /* need to allocate new atom */
|
||||
atom_ofs= shm_FragmentAlloc(&main_block->block,
|
||||
strlen(str)+sizeof(AtomData));
|
||||
if (atom_ofs==NIL)
|
||||
break; /* no more memory (atom==0) */
|
||||
ATOM_OFS(atom_idx)=atom_ofs;
|
||||
ptr=OFS2AtomData_ptr(atom_ofs);
|
||||
strcpy(ptr->str,str);
|
||||
ptr->count=1;
|
||||
atom=(ATOM)(atom_idx+MIN_STR_ATOM);
|
||||
break;
|
||||
default : /* has to update existing atom */
|
||||
OFS2AtomData_ptr(atom_ofs)->count++;
|
||||
atom=(ATOM)(atom_idx+MIN_STR_ATOM);
|
||||
} /* end of switch */
|
||||
} /* end of if */
|
||||
shm_write_signal(main_block->sem);
|
||||
return atom;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DDE_GlobalDeleteAtom
|
||||
*/
|
||||
|
||||
ATOM DDE_GlobalDeleteAtom( ATOM atom )
|
||||
{
|
||||
int atom_idx;
|
||||
int atom_ofs;
|
||||
AtomData_ptr atom_ptr;
|
||||
ATOM retval=(ATOM) 0;
|
||||
|
||||
TRACE("(\"%d\")\n",(int)atom);
|
||||
atom_idx=(int)atom - MIN_STR_ATOM;
|
||||
|
||||
if (atom_idx < 0 )
|
||||
return 0;
|
||||
|
||||
DDE_IPC_init(); /* will initialize only if needed */
|
||||
|
||||
shm_write_wait(main_block->sem);
|
||||
/* return used only once from here on -- for semaphore simplicity */
|
||||
switch (atom_ofs=ATOM_OFS(atom_idx)) {
|
||||
case DELETED:
|
||||
case EMPTY:
|
||||
WARN("Trying to free unallocated atom %d\n", atom);
|
||||
retval=atom;
|
||||
break;
|
||||
default :
|
||||
atom_ptr=OFS2AtomData_ptr(atom_ofs);
|
||||
if ( --atom_ptr->count == 0) {
|
||||
shm_FragmentFree(&main_block->block,atom_ofs);
|
||||
ATOM_OFS(atom_idx)=DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
shm_write_signal(main_block->sem);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DDE_GlobalFindAtom
|
||||
*/
|
||||
ATOM DDE_GlobalFindAtom( SEGPTR name )
|
||||
{
|
||||
int atom_idx;
|
||||
int atom_ofs;
|
||||
char *str;
|
||||
|
||||
TRACE("(%08lx)\n", name );
|
||||
|
||||
/* First check for integer atom */
|
||||
|
||||
if (!HIWORD(name)) return (ATOM)LOWORD(name);
|
||||
|
||||
str = (char *)PTR_SEG_TO_LIN( name );
|
||||
if (str[0] == '#')
|
||||
{
|
||||
ATOM atom= (ATOM) atoi(&str[1]);
|
||||
return (atom<MIN_STR_ATOM) ? atom : 0;
|
||||
}
|
||||
TRACE("(\"%s\")\n",str);
|
||||
|
||||
DDE_IPC_init(); /* will initialize only if needed */
|
||||
|
||||
shm_read_wait(main_block->sem);
|
||||
atom_idx=FindHash(str);
|
||||
if (atom_idx>=0)
|
||||
atom_ofs=ATOM_OFS(atom_idx); /* is it free ? */
|
||||
else
|
||||
atom_ofs=EMPTY;
|
||||
shm_read_signal(main_block->sem);
|
||||
|
||||
if (atom_ofs==EMPTY || atom_ofs==DELETED)
|
||||
return 0;
|
||||
else
|
||||
return (ATOM)(atom_idx+MIN_STR_ATOM);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DDE_GlobalGetAtomName
|
||||
*/
|
||||
WORD DDE_GlobalGetAtomName( ATOM atom, LPSTR buffer, short count )
|
||||
{
|
||||
int atom_idx, atom_ofs;
|
||||
int size;
|
||||
/* temporary buffer to hold maximum "#65535\0" */
|
||||
char str_num[7];
|
||||
|
||||
if (count<2) /* no sense to go on */
|
||||
return 0;
|
||||
atom_idx=(int)atom - MIN_STR_ATOM;
|
||||
|
||||
if (atom_idx < 0) { /* word atom */
|
||||
/* use wine convention... */
|
||||
sprintf(str_num,"#%d%n",(int)atom,&size);
|
||||
if (size+1>count) { /* overflow ? */
|
||||
/* truncate the string */
|
||||
size=count-1;
|
||||
str_num[size]='\0';
|
||||
}
|
||||
strcpy(buffer,str_num);
|
||||
return size;
|
||||
}
|
||||
|
||||
DDE_IPC_init(); /* will initialize only if needed */
|
||||
|
||||
/* string atom */
|
||||
shm_read_wait(main_block->sem);
|
||||
atom_ofs=ATOM_OFS(atom_idx);
|
||||
if (atom_ofs==EMPTY || atom_ofs==DELETED) {
|
||||
WARN("Illegal atom=%d\n",(int)atom);
|
||||
size=0;
|
||||
} else { /* non empty entry */
|
||||
/* string length will be at most count-1, find actual size */
|
||||
sprintf(buffer,"%.*s%n",count-1, OFS2AtomStr(atom_ofs), &size);
|
||||
}
|
||||
shm_read_signal(main_block->sem);
|
||||
return size;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,103 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_atom_test.c
|
||||
* Purpose : tests for dde_atom object
|
||||
***************************************************************************
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <win.h>
|
||||
#include "dde_atom.h"
|
||||
#include "shm_main_blk.h"
|
||||
#include "debugtools.h"
|
||||
|
||||
DECLARE_DEBUG_CHANNEL(atom)
|
||||
DECLARE_DEBUG_CHANNEL(sem)
|
||||
DECLARE_DEBUG_CHANNEL(shm)
|
||||
#define TOGETHER (DDE_ATOMS/5)
|
||||
|
||||
|
||||
/* run random sequences */
|
||||
int main()
|
||||
{
|
||||
ATOM atom_list[TOGETHER];
|
||||
char str[TOGETHER][80];
|
||||
int i,j,atom_n;
|
||||
int atom_len[TOGETHER];
|
||||
|
||||
TRACE_ON(shm)=1;
|
||||
TRACE_ON(atom)=0;
|
||||
TRACE_ON(sem)=0;
|
||||
|
||||
for (i=0 ; i<=10000/TOGETHER ; i++) {
|
||||
for (atom_n=0 ; atom_n<TOGETHER ; atom_n++) {
|
||||
atom_len[atom_n]=rand()%64+1;
|
||||
for (j=atom_len[atom_n]-1; j>=0; j--)
|
||||
do {
|
||||
str[atom_n][j]=(char)(rand()%255+1);
|
||||
} while (j==0 && str[atom_n][j]=='#');
|
||||
|
||||
str[atom_n][ atom_len[atom_n] ]='\0';
|
||||
|
||||
atom_list[atom_n]=GlobalAddAtom(str[atom_n]);
|
||||
|
||||
if (atom_list[atom_n]==0) {
|
||||
fprintf(stderr,"failed i=%d, atom_n=%d\n",i,atom_n);
|
||||
return 1;
|
||||
}
|
||||
if (atom_list[atom_n]!=GlobalAddAtom(str[atom_n])) {
|
||||
fprintf(stderr,
|
||||
"wrong second GlobalAddAtom(\"%s\")\n", str[atom_n]);
|
||||
return 1;
|
||||
}
|
||||
} /* for */
|
||||
for (atom_n=0 ; atom_n<TOGETHER ; atom_n++) {
|
||||
char buf[80];
|
||||
int len;
|
||||
|
||||
len=GlobalGetAtomName( atom_list[atom_n], buf, 79);
|
||||
if (atom_len[atom_n] != len) {
|
||||
fprintf(stderr, "i=%d, atom_n=%d; ", i, atom_n);
|
||||
fprintf(stderr,
|
||||
"wrong length of GlobalGetAtomName(\"%s\")\n",
|
||||
str[atom_n]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
for (atom_n=0 ; atom_n<TOGETHER ; atom_n++) {
|
||||
GlobalDeleteAtom(atom_list[atom_n]);
|
||||
if (atom_list[atom_n]!=GlobalAddAtom(str[atom_n])) {
|
||||
fprintf(stderr, "i=%d, atom_n=%d; ", i, atom_n);
|
||||
fprintf(stderr,
|
||||
"wrong third GlobalAddAtom(\"%s\")\n", str[atom_n]);
|
||||
return 1;
|
||||
}
|
||||
GlobalDeleteAtom(atom_list[atom_n]);
|
||||
GlobalDeleteAtom(atom_list[atom_n]);
|
||||
|
||||
atom_list[atom_n]=GlobalAddAtom(str[atom_n]);
|
||||
if (atom_list[atom_n]!=GlobalAddAtom(str[atom_n])) {
|
||||
fprintf(stderr,
|
||||
"i=%d, atom_n=%d wrong fifth GlobalAddAtom(\"%s\")\n",
|
||||
i, atom_n,
|
||||
str[atom_n]);
|
||||
return 1;
|
||||
}
|
||||
GlobalDeleteAtom(atom_list[atom_n]);
|
||||
if (atom_list[atom_n]!=GlobalFindAtom(str[atom_n])) {
|
||||
fprintf(stderr,
|
||||
"i=%d, atom_n=%d wrong GlobalFindAtom(\"%s\")\n",
|
||||
i, atom_n,
|
||||
str[atom_n]);
|
||||
return 1;
|
||||
}
|
||||
GlobalDeleteAtom(atom_list[atom_n]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
286
ipc/dde_mem.c
286
ipc/dde_mem.c
|
@ -1,286 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_mem.c
|
||||
* Purpose : shared DDE memory functionality for DDE
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <assert.h>
|
||||
#include "debugtools.h"
|
||||
#include "ldt.h"
|
||||
#include "shm_main_blk.h"
|
||||
#include "shm_fragment.h"
|
||||
#include "shm_semaph.h"
|
||||
#include "dde_mem.h"
|
||||
#include "bit_array.h"
|
||||
|
||||
DECLARE_DEBUG_CHANNEL(dde)
|
||||
DECLARE_DEBUG_CHANNEL(global)
|
||||
|
||||
#define SEGPTR2HANDLE_INFO(sptr) ( (struct handle_info*)PTR_SEG_TO_LIN(sptr) )
|
||||
|
||||
#define HINFO2DATAPTR(h_info_ptr) ( (void*) ( (char*)h_info_ptr + \
|
||||
sizeof(struct handle_info) ) )
|
||||
#define DDE_MEM_IDX(handle) ((handle)& 0x7fff)
|
||||
#define DDE_MEM_HANDLE(idx) ((idx) | 0x8000)
|
||||
#define DDE_MEM_INFO(handle) (main_block->handles[ DDE_MEM_IDX(handle) ])
|
||||
/* List of shared handles.
|
||||
* This entry resides on the shared memory, the data comes right
|
||||
* after the `handle_info'.
|
||||
* The entry is on the same block as the actual data.
|
||||
* The `next' field gives relative reference (relative to the start of
|
||||
* the blcok.
|
||||
*/
|
||||
struct handle_info {
|
||||
WORD lock_count;
|
||||
WORD flags;
|
||||
int size; /* size of the data (net)*/
|
||||
};
|
||||
|
||||
static bit_array free_handles;
|
||||
int debug_last_handle_size= 0; /* for debugging purpose only */
|
||||
|
||||
|
||||
/* locate_handle:
|
||||
* locate a shared memory handle.
|
||||
* Application:
|
||||
* The handle is first searched for in attached blocks.
|
||||
* At the beginning, only blocks owned by this process are
|
||||
* attached.
|
||||
* If a handle is not found, new blocks are attached.
|
||||
* Arguments:
|
||||
* h - the handle.
|
||||
* RETURN: pointer to handle info.
|
||||
*/
|
||||
static struct handle_info *locate_handle(HGLOBAL16 h, struct local_shm_map *map)
|
||||
{
|
||||
struct shm_block *block;
|
||||
|
||||
TRACE_(global)("shm: (0x%04x)\n", h);
|
||||
|
||||
|
||||
if (SampleBit( &free_handles, DDE_MEM_IDX(h)) == 0) {
|
||||
TRACE_(global)("shm: return NULL\n");
|
||||
return NULL; /* free!!! */
|
||||
}
|
||||
|
||||
block= shm_locate_block(DDE_MEM_INFO(h).shmid, map);
|
||||
if (block == NULL) {
|
||||
/* nothing found */
|
||||
TRACE_(global)("shm: return NULL\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (struct handle_info *) REL2PTR(block, DDE_MEM_INFO(h).rel);
|
||||
|
||||
}
|
||||
|
||||
/* dde_alloc_handle: allocate shared DDE handle */
|
||||
static HGLOBAL16 dde_alloc_handle()
|
||||
{
|
||||
int bit_nr;
|
||||
|
||||
bit_nr= AllocateBit( &free_handles);
|
||||
|
||||
if (bit_nr != -1)
|
||||
return DDE_MEM_HANDLE(bit_nr);
|
||||
|
||||
TRACE_(global)("dde_alloc_handle: no free DDE handle found\n");
|
||||
return 0;
|
||||
}
|
||||
/**********************************************************************
|
||||
* DDE_malloc
|
||||
*/
|
||||
void *
|
||||
DDE_malloc(unsigned int flags, unsigned long size, SHMDATA *shmdata)
|
||||
{
|
||||
int shmid;
|
||||
struct shm_block *block;
|
||||
struct handle_info *h_info;
|
||||
struct local_shm_map *curr;
|
||||
HGLOBAL16 handle;
|
||||
|
||||
TRACE_(global)("DDE_malloc flags %4X, size %ld\n", flags, size);
|
||||
DDE_IPC_init(); /* make sure main shm block allocated */
|
||||
|
||||
shm_write_wait(main_block->proc[curr_proc_idx].sem);
|
||||
|
||||
/* Try to find fragment big enough for `size' */
|
||||
/* iterate through all local shm blocks, and try to allocate
|
||||
the fragment */
|
||||
|
||||
h_info= NULL;
|
||||
for (curr= shm_map ; curr != NULL ; curr= curr->next) {
|
||||
if (curr->proc_idx == curr_proc_idx) {
|
||||
h_info= (struct handle_info *)
|
||||
shm_FragPtrAlloc(curr->ptr, size+sizeof(struct handle_info));
|
||||
if (h_info!=NULL) {
|
||||
shmid= curr->shm_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (h_info == NULL) {
|
||||
|
||||
block= shm_create_block(0, size+sizeof(struct handle_info), &shmid);
|
||||
if (block==NULL) {
|
||||
shm_write_signal(main_block->proc[curr_proc_idx].sem);
|
||||
return 0;
|
||||
}
|
||||
/* put the new block in the linked list */
|
||||
block->next_shm_id= main_block->proc[curr_proc_idx].shmid;
|
||||
main_block->proc[curr_proc_idx].shmid= shmid;
|
||||
h_info= (struct handle_info *)
|
||||
shm_FragPtrAlloc(block, size+sizeof(struct handle_info));
|
||||
if (h_info==NULL) {
|
||||
ERR_(global)("BUG! unallocated fragment\n");
|
||||
shm_write_signal(main_block->proc[curr_proc_idx].sem);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
block= curr->ptr;
|
||||
}
|
||||
|
||||
/* Here we have an allocated fragment */
|
||||
h_info->flags= flags;
|
||||
h_info->lock_count= 0;
|
||||
h_info->size= size;
|
||||
handle= dde_alloc_handle();
|
||||
|
||||
if (handle) {
|
||||
TRACE_(global)("returning handle=0x%4x, ptr=0x%08lx\n",
|
||||
(int)handle, (long) HINFO2DATAPTR(h_info));
|
||||
DDE_MEM_INFO(handle).rel= PTR2REL(block, h_info);
|
||||
DDE_MEM_INFO(handle).shmid= shmid;
|
||||
}
|
||||
else
|
||||
WARN_(global)("failed\n");
|
||||
|
||||
shm_write_signal(main_block->proc[curr_proc_idx].sem);
|
||||
|
||||
shmdata->handle= handle;
|
||||
return (char *)HINFO2DATAPTR(h_info);
|
||||
}
|
||||
|
||||
HGLOBAL16 DDE_GlobalFree(HGLOBAL16 h)
|
||||
{
|
||||
struct handle_info *h_info;
|
||||
int handle_index= h & 0x7fff;
|
||||
struct local_shm_map map;
|
||||
|
||||
TRACE_(global)("(0x%04x)\n",h);
|
||||
|
||||
if (h==0)
|
||||
return 0;
|
||||
|
||||
h_info= locate_handle(h, &map);
|
||||
if (h_info == NULL)
|
||||
return h;
|
||||
|
||||
shm_write_wait(main_block->proc[map.proc_idx].sem);
|
||||
|
||||
shm_FragPtrFree(map.ptr, (struct shm_fragment *) h_info);
|
||||
|
||||
AssignBit( &free_handles, handle_index, 0);
|
||||
|
||||
/* FIXME: must free the shm block some day. */
|
||||
shm_write_signal(main_block->proc[map.proc_idx].sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WORD DDE_SyncHandle(HGLOBAL16 handle, WORD sel)
|
||||
|
||||
{
|
||||
struct handle_info *h_info;
|
||||
void *local_ptr;
|
||||
ldt_entry entry;
|
||||
|
||||
h_info= locate_handle(handle, NULL);
|
||||
local_ptr= (void *)GET_SEL_BASE(sel);
|
||||
|
||||
|
||||
if (h_info == NULL)
|
||||
return 0;
|
||||
|
||||
if (local_ptr == (void *) HINFO2DATAPTR(h_info))
|
||||
return sel;
|
||||
|
||||
/* need syncronization ! */
|
||||
LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
|
||||
entry.base= (unsigned long) HINFO2DATAPTR(h_info);
|
||||
LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
/*
|
||||
* DDE_AttachHandle:
|
||||
* Attach shm memory (The data must not be already attached).
|
||||
* Parameters:
|
||||
* handle - the memory to attach.
|
||||
* segptr - in not null, return SEGPTR to the same block.
|
||||
* return value:
|
||||
* 32 bit pointer to the memory.
|
||||
*/
|
||||
|
||||
void *DDE_AttachHandle(HGLOBAL16 handle, SEGPTR *segptr)
|
||||
{
|
||||
struct handle_info *h_info;
|
||||
SHMDATA shmdata;
|
||||
void *ptr;
|
||||
HGLOBAL16 hOwner = GetCurrentPDB16();
|
||||
|
||||
assert(is_dde_handle(handle));
|
||||
if (segptr != NULL)
|
||||
*segptr=0;
|
||||
|
||||
TRACE_(global)("(%04x)\n",handle);
|
||||
h_info=locate_handle(handle, NULL);
|
||||
|
||||
if (h_info == NULL)
|
||||
return NULL;
|
||||
|
||||
if ( !(h_info->flags & GMEM_DDESHARE) ) {
|
||||
ERR_(global)("Corrupted memory handle info\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRACE_(global)("h_info=%06lx\n",(long)h_info);
|
||||
|
||||
shmdata.handle= handle;
|
||||
shmdata.shmid= DDE_MEM_INFO(handle).shmid;
|
||||
|
||||
ptr= HINFO2DATAPTR(h_info);
|
||||
/* Allocate the selector(s) */
|
||||
if (! GLOBAL_CreateBlock( h_info->flags, ptr, h_info->size, hOwner,
|
||||
FALSE, FALSE, FALSE, &shmdata))
|
||||
return NULL;
|
||||
|
||||
if (segptr != NULL)
|
||||
*segptr= (SEGPTR)MAKELONG( 0, shmdata.sel);
|
||||
|
||||
if (TRACE_ON(dde))
|
||||
debug_last_handle_size= h_info->size;
|
||||
|
||||
TRACE_(global)("DDE_AttachHandle returns ptr=0x%08lx\n", (long)ptr);
|
||||
|
||||
return (LPSTR)ptr;
|
||||
|
||||
}
|
||||
|
||||
void DDE_mem_init()
|
||||
{
|
||||
int nr_of_bits;
|
||||
|
||||
shm_init();
|
||||
|
||||
nr_of_bits= BITS_PER_BYTE * sizeof(main_block->free_handles);
|
||||
AssembleArray( &free_handles, main_block->free_handles, nr_of_bits);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,73 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_mem_test.c
|
||||
* Purpose : test shared DDE memory functionality for DDE
|
||||
* Usage: Look for assertion failures
|
||||
***************************************************************************
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <win.h>
|
||||
#include "dde_mem.h"
|
||||
/* stub */
|
||||
|
||||
void ATOM_GlobalInit()
|
||||
{
|
||||
printf("ATOM_GlobalInit\n");
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
HWND h1,h2,h3;
|
||||
int ret;
|
||||
void *p1,*p2,*p3,*p;
|
||||
SHMDATA shmdata;
|
||||
|
||||
/* alloc h1, h2, h3 */
|
||||
|
||||
setbuf(stdout,NULL);
|
||||
p1=DDE_malloc(GMEM_DDESHARE, 0x6000, &shmdata);
|
||||
h1= shmdata.handle;
|
||||
assert(p1 != NULL);
|
||||
assert(h1 != 0);
|
||||
p2=DDE_malloc(GMEM_DDESHARE, 0xff00, &shmdata);
|
||||
h2= shmdata.handle;
|
||||
assert(p2 != NULL);
|
||||
assert(h2 != 0);
|
||||
p3=DDE_malloc(GMEM_DDESHARE, 0x6000, &shmdata);
|
||||
h3= shmdata.handle;
|
||||
assert(p3 != 0);
|
||||
assert(h3 != 0);
|
||||
|
||||
/* lock h1, h2, h3 */
|
||||
p=DDE_AttachHandle(h1,NULL);
|
||||
assert(p1==p);
|
||||
p=DDE_AttachHandle(h2,NULL);
|
||||
assert(p2==p);
|
||||
p=DDE_AttachHandle(h3,NULL);
|
||||
assert(p3==p);
|
||||
|
||||
|
||||
|
||||
ret=DDE_GlobalFree(h1);
|
||||
assert(ret==0);
|
||||
/* do some implementation dependant tests */
|
||||
p=DDE_malloc(GMEM_DDESHARE, 0x6000, &shmdata);
|
||||
assert(p!=NULL);
|
||||
assert(shmdata.handle==h1);
|
||||
p=DDE_AttachHandle(h1,NULL);
|
||||
assert(p1==p);
|
||||
|
||||
/* check freeing */
|
||||
ret=DDE_GlobalFree(h1);
|
||||
assert(ret==0);
|
||||
ret=DDE_GlobalFree(h2);
|
||||
assert(ret==0);
|
||||
ret=DDE_GlobalFree(h3);
|
||||
assert(ret==0);
|
||||
return 0;
|
||||
}
|
719
ipc/dde_proc.c
719
ipc/dde_proc.c
|
@ -1,719 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_proc.c
|
||||
* Purpose : DDE signals and processes functionality for DDE
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define msgbuf mymsg
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/msg.h>
|
||||
#include "windef.h"
|
||||
#include "win.h"
|
||||
#include "shm_semaph.h"
|
||||
#include "shm_main_blk.h"
|
||||
#include "dde_proc.h"
|
||||
#include "dde_mem.h"
|
||||
#include "dde.h"
|
||||
#include "debugtools.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
DECLARE_DEBUG_CHANNEL(dde)
|
||||
DECLARE_DEBUG_CHANNEL(msg)
|
||||
|
||||
int curr_proc_idx= -1;
|
||||
|
||||
enum stop_wait_op stop_wait_op=CONT;
|
||||
int had_SIGUSR2 = 0;
|
||||
sigjmp_buf env_get_ack;
|
||||
sigjmp_buf env_wait_x;
|
||||
|
||||
#define IDX_TO_HWND(idx) (0xfffe - (idx))
|
||||
#define HWND_TO_IDX(wnd) (0xfffe - (wnd))
|
||||
#define DDE_WIN_INFO(win) ( main_block->windows[HWND_TO_IDX(win)] )
|
||||
#define DDE_WIN2PROC(win) ( DDE_WIN_INFO(win).proc_idx )
|
||||
#define DDE_IsRemoteWindow(win) ( (win)<0xffff && (win)>=(0xffff-DDE_PROCS))
|
||||
#define DDE_SEND 1
|
||||
#define DDE_POST 2
|
||||
#define DDE_ACK 3
|
||||
#define DDE_MSG_SIZE sizeof(MSG16)
|
||||
#define FREE_WND (WORD)(-2)
|
||||
#define DELETED_WND (WORD)(-3)
|
||||
static char *msg_type[4]={"********", "DDE_SEND", "DDE_POST", "DDE_ACK"};
|
||||
|
||||
struct msg_dat {
|
||||
struct msgbuf dat;
|
||||
char filler[DDE_MSG_SIZE];
|
||||
} ;
|
||||
|
||||
typedef struct fifo_element {
|
||||
int value;
|
||||
struct fifo_element *next;
|
||||
} fifo_element;
|
||||
|
||||
struct fifo {
|
||||
fifo_element *first; /* first element in the fifo or NULL */
|
||||
fifo_element *last; /* last element in the fifo or NULL */
|
||||
};
|
||||
static struct fifo fifo = {NULL,NULL};
|
||||
|
||||
void dde_proc_delete(int proc_idx);
|
||||
|
||||
void dde_proc_add_fifo(int val)
|
||||
{
|
||||
fifo_element *created;
|
||||
|
||||
created= (fifo_element*) xmalloc( sizeof(fifo_element) );
|
||||
created->value = val;
|
||||
created->next = NULL;
|
||||
|
||||
if (fifo.first==NULL)
|
||||
fifo.first= created;
|
||||
else
|
||||
fifo.last->next= created;
|
||||
fifo.last = created;
|
||||
}
|
||||
|
||||
/* get an item from the fifo, and return it.
|
||||
* If fifo is empty, return -1
|
||||
*/
|
||||
int dde_proc_shift_fifo()
|
||||
{
|
||||
int val;
|
||||
fifo_element *deleted;
|
||||
|
||||
if (fifo.first == NULL)
|
||||
return -1;
|
||||
|
||||
deleted= fifo.first;
|
||||
val= deleted->value;
|
||||
fifo.first= deleted->next;
|
||||
if (fifo.first == NULL)
|
||||
fifo.last= NULL;
|
||||
|
||||
free(deleted);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void print_dde_message(char *desc, MSG16 *msg);
|
||||
|
||||
/* This should be run only when main_block is first allocated. */
|
||||
void dde_proc_init(dde_proc proc)
|
||||
{
|
||||
int proc_num;
|
||||
|
||||
for (proc_num=0 ; proc_num<DDE_PROCS ; proc_num++, proc++) {
|
||||
proc->msg=-1;
|
||||
proc->sem=-1;
|
||||
proc->shmid=-1;
|
||||
proc->pid=-1;
|
||||
}
|
||||
}
|
||||
|
||||
/* add current process to the list of processes */
|
||||
void dde_proc_add(dde_proc procs)
|
||||
{
|
||||
dde_proc proc;
|
||||
int proc_idx;
|
||||
TRACE_(dde)("(..)\n");
|
||||
shm_write_wait(main_block->sem);
|
||||
|
||||
/* find free proc_idx and allocate it */
|
||||
for (proc_idx=0, proc=procs ; proc_idx<DDE_PROCS ; proc_idx++, proc++)
|
||||
if (proc->pid==-1)
|
||||
break; /* found! */
|
||||
|
||||
if (proc_idx<DDE_PROCS) { /* got here beacuse a free was found ? */
|
||||
dde_msg_setup(&proc->msg);
|
||||
proc->pid=getpid();
|
||||
curr_proc_idx=proc_idx;
|
||||
shm_sem_init(&proc->sem);
|
||||
}
|
||||
else {
|
||||
fflush(stdout);
|
||||
WARN_(dde)("Can't allocate process\n");
|
||||
}
|
||||
shm_write_signal(main_block->sem);
|
||||
}
|
||||
|
||||
/* wait for dde - acknowledge message - or timout */
|
||||
static BOOL get_ack()
|
||||
{
|
||||
struct timeval timeout;
|
||||
int size;
|
||||
struct msg_dat ack_buff;
|
||||
|
||||
/* timeout after exactly one seconf */
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
sigsetjmp(env_get_ack, 1);
|
||||
/* get here after normal execution, or after siglongjmp */
|
||||
|
||||
do { /* loop to wait for DDE_ACK */
|
||||
had_SIGUSR2=0;
|
||||
stop_wait_op=CONT; /* sensitive code: disallow siglongjmp */
|
||||
size= msgrcv( main_block->proc[curr_proc_idx].msg , &ack_buff.dat,
|
||||
1, DDE_ACK, IPC_NOWAIT);
|
||||
if (size>=0) {
|
||||
TRACE_(msg)("get_ack: received DDE_ACK message\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (DDE_GetRemoteMessage()) {
|
||||
had_SIGUSR2=1; /* might have recieved SIGUSR2 */
|
||||
}
|
||||
stop_wait_op=STOP_WAIT_ACK; /* allow siglongjmp */
|
||||
|
||||
} while (had_SIGUSR2); /* loop if SIGUSR2 was recieved */
|
||||
|
||||
/* siglongjmp should be enabled at this moment */
|
||||
select( 0, NULL, NULL, NULL, &timeout );
|
||||
stop_wait_op=CONT; /* disallow further siglongjmp */
|
||||
|
||||
/* timeout !! (otherwise there would have been a siglongjmp) */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Transfer one message to a given process */
|
||||
static BOOL DDE_DoOneMessage (int proc_idx, int size, struct msgbuf *msgbuf)
|
||||
{
|
||||
dde_proc proc= &main_block->proc[ proc_idx ];
|
||||
|
||||
|
||||
if (proc_idx == curr_proc_idx)
|
||||
return FALSE;
|
||||
|
||||
if (kill(proc->pid,0) < 0) {
|
||||
/* pid does not exist, or not our */
|
||||
dde_proc_delete(proc_idx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (TRACE_ON(dde) || WARN_ON_dde) {
|
||||
MSG16 *msg=(MSG16*) &msgbuf->mtext;
|
||||
char *title;
|
||||
if (msgbuf->mtype==DDE_SEND)
|
||||
title="sending dde:";
|
||||
else if (msgbuf->mtype==DDE_POST)
|
||||
title="posting dde:";
|
||||
else
|
||||
title=NULL;
|
||||
if (title)
|
||||
print_dde_message(title, msg);
|
||||
else
|
||||
WARN_(dde)("Unknown message type=0x%lx\n", msgbuf->mtype);
|
||||
}
|
||||
TRACE_(msg)("to proc_idx=%d (pid=%d), queue=%u\n",
|
||||
proc_idx, proc->pid, (unsigned)proc->msg);
|
||||
if ( proc->msg != -1) {
|
||||
TRACE_(msg)("doing...(type=%s)\n", msg_type[msgbuf->mtype]);
|
||||
size=msgsnd (proc->msg, msgbuf, size, 0);
|
||||
|
||||
if (size<0) {
|
||||
fflush(stdout);
|
||||
perror("msgsnd");
|
||||
}
|
||||
kill(proc->pid,SIGUSR2); /* tell the process there is a message */
|
||||
|
||||
TRACE_(msg)("Trying to get acknowledgment from msg queue=%d\n",
|
||||
proc->msg);
|
||||
Yield16(); /* force task switch, and */
|
||||
/* acknowledgment sending */
|
||||
if (get_ack()) {
|
||||
return TRUE;
|
||||
} else {
|
||||
fflush(stdout);
|
||||
WARN_(dde)("get_ack: DDE_DoOneMessage: timeout\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
WARN_(msg)("message not sent, target has no message queue\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do some sort of premitive hash table */
|
||||
static HWND16 HWND_Local2Remote(HWND16 orig)
|
||||
{
|
||||
int dde_wnd_idx;
|
||||
int deleted_idx= -1;
|
||||
WND_DATA *tested;
|
||||
WND_DATA *deleted= NULL;
|
||||
int i;
|
||||
|
||||
dde_wnd_idx= orig % DDE_WINDOWS;
|
||||
for ( i=0 ; i < DDE_WINDOWS ; i++, dde_wnd_idx++) {
|
||||
if (dde_wnd_idx >= DDE_WINDOWS)
|
||||
dde_wnd_idx -= DDE_WINDOWS; /* wrap-around */
|
||||
|
||||
tested= &main_block->windows[ dde_wnd_idx ];
|
||||
if (tested->proc_idx == FREE_WND)
|
||||
break;
|
||||
|
||||
if (deleted == NULL && tested->proc_idx == DELETED_WND) {
|
||||
deleted= tested;
|
||||
deleted_idx= dde_wnd_idx;
|
||||
} else if (tested->wnd == orig && tested->proc_idx == curr_proc_idx) {
|
||||
return IDX_TO_HWND(dde_wnd_idx);
|
||||
}
|
||||
}
|
||||
if (deleted != NULL) { /* deleted is preferable */
|
||||
/* free item, allocate it */
|
||||
deleted->proc_idx= curr_proc_idx;
|
||||
deleted->wnd = orig;
|
||||
return IDX_TO_HWND(deleted_idx);
|
||||
}
|
||||
if (tested->proc_idx == FREE_WND) {
|
||||
tested->proc_idx= curr_proc_idx;
|
||||
tested->wnd = orig;
|
||||
return IDX_TO_HWND(dde_wnd_idx);
|
||||
}
|
||||
|
||||
WARN_(dde)("Can't map any more windows to DDE windows\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL DDE_DoMessage( MSG16 *msg, int type )
|
||||
{
|
||||
int proc_idx;
|
||||
|
||||
MSG16 *remote_message;
|
||||
struct msg_dat msg_dat;
|
||||
BOOL success;
|
||||
|
||||
if (msg->wParam == 0)
|
||||
return FALSE;
|
||||
|
||||
if (main_block==NULL) {
|
||||
if (msg->message >= WM_DDE_FIRST && msg->message <= WM_DDE_LAST)
|
||||
DDE_IPC_init();
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (msg->wParam == (HWND16)-1)
|
||||
return FALSE;
|
||||
|
||||
if ( ! DDE_IsRemoteWindow(msg->hwnd) && msg->hwnd!= (HWND16)-1)
|
||||
return FALSE;
|
||||
|
||||
TRACE_(msg)("(hwnd=0x%x,msg=0x%x,..) - %s\n",
|
||||
(int)msg->hwnd,(int)msg->message,msg_type[type]);
|
||||
|
||||
|
||||
TRACE_(msg)("(hwnd=0x%x,msg=0x%x,..) -- HWND_BROADCAST !\n",
|
||||
(int)msg->hwnd,(int)msg->message);
|
||||
remote_message=(void*)&msg_dat.dat.mtext;
|
||||
|
||||
memcpy(remote_message, msg, sizeof(*msg));
|
||||
remote_message->wParam= HWND_Local2Remote(msg->wParam);
|
||||
if (remote_message->wParam == 0)
|
||||
return FALSE;
|
||||
|
||||
msg_dat.dat.mtype=type;
|
||||
|
||||
if (msg->hwnd == (HWND16)-1) {
|
||||
success= FALSE;
|
||||
for ( proc_idx=0; proc_idx < DDE_PROCS ; proc_idx++) {
|
||||
if (proc_idx == curr_proc_idx)
|
||||
continue;
|
||||
if (main_block->proc[ proc_idx ].msg != -1)
|
||||
success|=DDE_DoOneMessage(proc_idx, DDE_MSG_SIZE, &msg_dat.dat);
|
||||
}
|
||||
return success;
|
||||
} else {
|
||||
return DDE_DoOneMessage(DDE_WIN2PROC(msg->hwnd), DDE_MSG_SIZE,
|
||||
&msg_dat.dat);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL DDE_SendMessage( MSG16 *msg)
|
||||
{
|
||||
return DDE_DoMessage(msg, DDE_SEND);
|
||||
}
|
||||
|
||||
BOOL DDE_PostMessage( MSG16 *msg)
|
||||
{
|
||||
return DDE_DoMessage(msg, DDE_POST);
|
||||
}
|
||||
|
||||
|
||||
void dde_proc_send_ack(HWND16 wnd, BOOL val) {
|
||||
int proc,msg;
|
||||
|
||||
static struct msgbuf msg_ack={DDE_ACK,{'0'}};
|
||||
|
||||
proc=DDE_WIN2PROC(wnd);
|
||||
msg=main_block->proc[proc].msg;
|
||||
TRACE_(msg)("sending ACK to wnd=%4x, proc=%d,msg=%d, pid=%d\n",
|
||||
wnd,proc,msg,main_block->proc[proc].pid);
|
||||
|
||||
msg_ack.mtext[0]=val;
|
||||
msgsnd (msg, &msg_ack, 1, 0);
|
||||
kill(main_block->proc[proc].pid, SIGUSR2);
|
||||
}
|
||||
|
||||
/* return true (non zero) if had a remote message */
|
||||
#undef DDE_GetRemoteMessage
|
||||
|
||||
int DDE_GetRemoteMessage()
|
||||
{
|
||||
static int nesting=0; /* to avoid infinite recursion */
|
||||
|
||||
MSG16 *remote_message;
|
||||
int size;
|
||||
struct msg_dat msg_dat;
|
||||
BOOL was_sent; /* sent/received */
|
||||
BOOL passed;
|
||||
WND *wndPtr;
|
||||
|
||||
if (curr_proc_idx==-1) /* do we have DDE initialized ? */
|
||||
return 0;
|
||||
|
||||
if (nesting>10) {
|
||||
fflush(stdout);
|
||||
ERR_(msg)("suspecting infinite recursion, exiting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
remote_message=(void*)&msg_dat.dat.mtext;
|
||||
|
||||
/* test for SendMessage */
|
||||
size= msgrcv( main_block->proc[curr_proc_idx].msg , &msg_dat.dat,
|
||||
DDE_MSG_SIZE, DDE_SEND, IPC_NOWAIT);
|
||||
|
||||
if (size==DDE_MSG_SIZE) { /* is this a correct message (if any) ?*/
|
||||
was_sent=TRUE;
|
||||
TRACE_(msg)("DDE:receive sent message. msg=%04x wPar=%04x"
|
||||
" lPar=%08lx\n",
|
||||
remote_message->message, remote_message->wParam,
|
||||
remote_message->lParam);
|
||||
} else {
|
||||
size= msgrcv( main_block->proc[curr_proc_idx].msg , &msg_dat.dat,
|
||||
DDE_MSG_SIZE, DDE_POST, IPC_NOWAIT);
|
||||
|
||||
if (size==DDE_MSG_SIZE) { /* is this a correct message (if any) ?*/
|
||||
was_sent=FALSE;
|
||||
TRACE_(msg)("DDE:receive posted message. "
|
||||
"msg=%04x wPar=%04x lPar=%08lx\n",
|
||||
remote_message->message, remote_message->wParam,
|
||||
remote_message->lParam);
|
||||
}
|
||||
else
|
||||
return 0; /* no DDE message found */
|
||||
}
|
||||
|
||||
/* At this point we are sure that there is a DDE message,
|
||||
* was_sent is TRUE is the message was sent, and false if it was posted
|
||||
*/
|
||||
|
||||
nesting++;
|
||||
|
||||
if (TRACE_ON(dde)) {
|
||||
char *title;
|
||||
if (was_sent)
|
||||
title="receive sent dde:";
|
||||
else
|
||||
title="receive posted dde:";
|
||||
print_dde_message(title, remote_message);
|
||||
}
|
||||
|
||||
if (remote_message->hwnd != (HWND16) -1 ) {
|
||||
HWND16 dde_window= DDE_WIN_INFO(remote_message->hwnd).wnd;
|
||||
/* we should know exactly where to send the message (locally)*/
|
||||
if (was_sent) {
|
||||
TRACE_(dde)("SendMessage(wnd=0x%04x, msg=0x%04x, wPar=0x%04x,"
|
||||
"lPar=0x%08x\n", dde_window, remote_message->message,
|
||||
remote_message->wParam, (int)remote_message->lParam);
|
||||
|
||||
/* execute the recieved message */
|
||||
passed= SendMessage16(dde_window, remote_message->message,
|
||||
remote_message->wParam, remote_message->lParam);
|
||||
|
||||
/* Tell the sended, that the message is here */
|
||||
dde_proc_send_ack(remote_message->wParam, passed);
|
||||
}
|
||||
else {
|
||||
passed= PostMessage16(dde_window, remote_message->message,
|
||||
remote_message->wParam, remote_message->lParam);
|
||||
if (passed == FALSE) {
|
||||
/* Tell the sender, that the message is here, and failed */
|
||||
dde_proc_send_ack(remote_message->wParam, FALSE);
|
||||
}
|
||||
else {
|
||||
/* ack will be sent later, at the first peek/get message */
|
||||
dde_proc_add_fifo(remote_message->wParam);
|
||||
}
|
||||
}
|
||||
nesting--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* iterate through all the windows */
|
||||
for (wndPtr = WIN_FindWndPtr(GetTopWindow(GetDesktopWindow()));
|
||||
wndPtr != NULL;
|
||||
WIN_UpdateWndPtr(&wndPtr,wndPtr->next))
|
||||
{
|
||||
if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) {
|
||||
if (was_sent)
|
||||
SendMessage16( wndPtr->hwndSelf, remote_message->message,
|
||||
remote_message->wParam, remote_message->lParam );
|
||||
else
|
||||
PostMessage16( wndPtr->hwndSelf, remote_message->message,
|
||||
remote_message->wParam, remote_message->lParam );
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* replay with DDE_ACK after broadcasting in DDE_GetRemoteMessage */
|
||||
dde_proc_send_ack(remote_message->wParam, TRUE);
|
||||
|
||||
nesting--;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dde_reschedule()
|
||||
{
|
||||
int ack_wnd;
|
||||
|
||||
ack_wnd= dde_proc_shift_fifo();
|
||||
if (ack_wnd != -1) {
|
||||
dde_proc_send_ack(ack_wnd, TRUE);
|
||||
usleep(10000); /* force unix task switch */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void dde_msg_setup(int *msg_ptr)
|
||||
{
|
||||
*msg_ptr= msgget (IPC_PRIVATE, IPC_CREAT | 0700);
|
||||
if (*msg_ptr==-1)
|
||||
perror("dde_msg_setup fails to get message queue");
|
||||
}
|
||||
|
||||
/* do we have dde handling in the window ?
|
||||
* If we have, atom usage will make this instance of wine set up
|
||||
* it's IPC stuff.
|
||||
*/
|
||||
void DDE_TestDDE(HWND16 hwnd)
|
||||
{
|
||||
static in_test = 0;
|
||||
if (in_test++) return;
|
||||
if (main_block != NULL) {
|
||||
in_test--;
|
||||
return;
|
||||
}
|
||||
TRACE_(msg)("(0x%04x)\n", hwnd);
|
||||
if (hwnd==0)
|
||||
hwnd=-1;
|
||||
/* just send a message to see how things are going */
|
||||
SendMessage16( hwnd, WM_DDE_INITIATE, 0, 0);
|
||||
in_test--;
|
||||
}
|
||||
|
||||
void dde_proc_delete(int proc_idx)
|
||||
{
|
||||
dde_proc_done(&main_block->proc[proc_idx]);
|
||||
}
|
||||
void stop_wait(int a)
|
||||
{
|
||||
|
||||
had_SIGUSR2=1;
|
||||
switch(stop_wait_op) {
|
||||
case STOP_WAIT_ACK:
|
||||
siglongjmp(env_get_ack,1);
|
||||
break; /* never reached */
|
||||
case STOP_WAIT_X:
|
||||
siglongjmp(env_wait_x,1);
|
||||
break; /* never reached */
|
||||
case CONT:
|
||||
/* do nothing */
|
||||
}
|
||||
}
|
||||
|
||||
static void print_dde_message(char *desc, MSG16 *msg)
|
||||
{
|
||||
/* extern const char *MessageTypeNames[];*/
|
||||
extern int debug_last_handle_size;
|
||||
WORD wStatus,hWord;
|
||||
void *ptr;
|
||||
DDEACK *ddeack;
|
||||
DDEADVISE *ddeadvise;
|
||||
DDEDATA *ddedata;
|
||||
DDEPOKE *ddepoke;
|
||||
dbg_decl_str(dde, 2048);
|
||||
|
||||
if (is_dde_handle(msg->lParam & 0xffff) )
|
||||
ptr=DDE_AttachHandle(msg->lParam&0xffff, NULL);
|
||||
else
|
||||
ptr =NULL;
|
||||
wStatus=LOWORD(msg->lParam);
|
||||
hWord=HIWORD(msg->lParam);
|
||||
|
||||
dsprintf(dde,"%s", desc);
|
||||
dsprintf(dde,"%04x %04x==%s %04x %08lx ",
|
||||
msg->hwnd, msg->message,"",/*MessageTypeNames[msg->message],*/
|
||||
msg->wParam, msg->lParam);
|
||||
switch(msg->message) {
|
||||
case WM_DDE_INITIATE:
|
||||
case WM_DDE_REQUEST:
|
||||
case WM_DDE_EXECUTE:
|
||||
case WM_DDE_TERMINATE:
|
||||
/* nothing to do */
|
||||
break;
|
||||
case WM_DDE_ADVISE:
|
||||
/* DDEADVISE: hOptions in WM_DDE_ADVISE message */
|
||||
if (ptr) {
|
||||
ddeadvise=ptr;
|
||||
dsprintf(dde,"fDeferUpd=%d,fAckReq=%d,cfFormat=0x%x",
|
||||
ddeadvise->fDeferUpd, ddeadvise->fAckReq,
|
||||
ddeadvise->cfFormat);
|
||||
} else
|
||||
dsprintf(dde,"NO-DATA");
|
||||
dsprintf(dde," atom=0x%x",hWord);
|
||||
break;
|
||||
|
||||
case WM_DDE_UNADVISE:
|
||||
dsprintf(dde,"format=0x%x, atom=0x%x",wStatus,hWord);
|
||||
break;
|
||||
case WM_DDE_ACK:
|
||||
ddeack=(DDEACK*)&wStatus;
|
||||
dsprintf(dde,"bAppReturnCode=%d,fBusy=%d,fAck=%d",
|
||||
ddeack->bAppReturnCode, ddeack->fBusy, ddeack->fAck);
|
||||
if (ddeack->fAck)
|
||||
dsprintf(dde,"(True)");
|
||||
else
|
||||
dsprintf(dde,"(False)");
|
||||
break;
|
||||
|
||||
case WM_DDE_DATA:
|
||||
if (ptr) {
|
||||
ddedata=ptr;
|
||||
dsprintf(dde,"fResponse=%d,fRelease=%d,"
|
||||
"fAckReq=%d,cfFormat=0x%x,value=\"%.*s\"",
|
||||
ddedata->fResponse, ddedata->fRelease,
|
||||
ddedata->fAckReq, ddedata->cfFormat,
|
||||
debug_last_handle_size- (int)sizeof(*ddedata)+1,
|
||||
ddedata->Value);
|
||||
} else
|
||||
dsprintf(dde,"NO-DATA");
|
||||
dsprintf(dde," atom=0x%04x",hWord);
|
||||
break;
|
||||
|
||||
case WM_DDE_POKE:
|
||||
if (ptr) {
|
||||
ddepoke=ptr;
|
||||
dsprintf(dde,"fRelease=%d,cfFormat=0x%x,value[0]='%c'",
|
||||
ddepoke->fRelease, ddepoke->cfFormat, ddepoke->Value[0]);
|
||||
} else
|
||||
dsprintf(dde,"NO-DATA");
|
||||
dsprintf(dde," atom=0x%04x",hWord);
|
||||
break;
|
||||
}
|
||||
TRACE_(dde)("%s\n", dbg_str(dde));
|
||||
}
|
||||
|
||||
void dde_proc_done(dde_proc proc)
|
||||
{
|
||||
if (proc->msg != -1)
|
||||
msgctl(proc->msg, IPC_RMID, NULL);
|
||||
proc->msg=-1;
|
||||
proc->pid=-1;
|
||||
shm_delete_chain(&proc->shmid);
|
||||
shm_sem_done(&proc->sem);
|
||||
}
|
||||
|
||||
/* delete entry, if old junk */
|
||||
void dde_proc_refresh(dde_proc proc)
|
||||
{
|
||||
if (proc->pid == -1)
|
||||
return;
|
||||
|
||||
if (kill(proc->pid, 0) != -1)
|
||||
return;
|
||||
|
||||
/* get here if entry non empty, and the process does not exist */
|
||||
dde_proc_done(proc);
|
||||
}
|
||||
|
||||
void dde_wnd_setup()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i < DDE_WINDOWS ; i++)
|
||||
main_block->windows[i].proc_idx = FREE_WND;
|
||||
}
|
||||
|
||||
static BOOL DDE_ProcHasWindows(int proc_idx)
|
||||
{
|
||||
WND_DATA *tested;
|
||||
int i;
|
||||
|
||||
for ( i=0 ; i < DDE_WINDOWS ; i++) {
|
||||
tested= &main_block->windows[ i ];
|
||||
|
||||
if (tested->proc_idx == proc_idx)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
/* Look for hwnd in the hash table of DDE windows,
|
||||
* Delete it from there. If there are no more windows for this
|
||||
* process, remove the process from the DDE data-structure.
|
||||
* If there are no more processes - delete the whole DDE struff.
|
||||
*
|
||||
* This is inefficient, but who cares for the efficiency of this rare
|
||||
* operation...
|
||||
*/
|
||||
void DDE_DestroyWindow(HWND16 hwnd)
|
||||
{
|
||||
int dde_wnd_idx;
|
||||
WND_DATA *tested;
|
||||
int i;
|
||||
|
||||
if (main_block == NULL)
|
||||
return;
|
||||
|
||||
dde_wnd_idx= hwnd % DDE_WINDOWS;
|
||||
|
||||
for ( i=0 ; i < DDE_WINDOWS ; i++, dde_wnd_idx++) {
|
||||
if (dde_wnd_idx >= DDE_WINDOWS)
|
||||
dde_wnd_idx -= DDE_WINDOWS; /* wrap-around */
|
||||
|
||||
tested= &main_block->windows[ dde_wnd_idx ];
|
||||
if (tested->proc_idx == FREE_WND)
|
||||
return; /* No window will get deleted here */
|
||||
|
||||
if (tested->wnd == hwnd && tested->proc_idx == curr_proc_idx) {
|
||||
dde_reschedule();
|
||||
tested->proc_idx= DELETED_WND;
|
||||
if (DDE_ProcHasWindows( curr_proc_idx ))
|
||||
return;
|
||||
while (dde_reschedule()) /* make sure there are no other */
|
||||
/* processes waiting for acknowledgment */
|
||||
;
|
||||
dde_proc_delete( curr_proc_idx );
|
||||
if (DDE_no_of_attached() == 1)
|
||||
shm_delete_all(-1);
|
||||
else {
|
||||
shmdt( (void *) main_block);
|
||||
main_block= NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,117 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: dde_proc.c
|
||||
* Purpose : test DDE signals and processes functionality for DDE
|
||||
* Usage: run two independant processes, one with an argument another
|
||||
* without (with the argument is the server).
|
||||
***************************************************************************
|
||||
*/
|
||||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/param.h>
|
||||
#else
|
||||
#include <syscall.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <win.h>
|
||||
#include "dde.h"
|
||||
#include "dde_proc.h"
|
||||
#include "shm_main_blk.h"
|
||||
|
||||
#if !defined(BSD4_4) || defined(linux) || defined(__FreeBSD__)
|
||||
char * cstack[4096];
|
||||
#endif
|
||||
#ifdef linux
|
||||
extern void ___sig_restore();
|
||||
extern void ___masksig_restore();
|
||||
|
||||
/* Similar to the sigaction function in libc, except it leaves alone the
|
||||
restorer field */
|
||||
|
||||
static int
|
||||
wine_sigaction(int sig,struct sigaction * new, struct sigaction * old)
|
||||
{
|
||||
__asm__("int $0x80":"=a" (sig)
|
||||
:"0" (SYS_sigaction),"b" (sig),"c" (new),"d" (old));
|
||||
if (sig>=0)
|
||||
return 0;
|
||||
errno = -sig;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sigaction usr2_act;
|
||||
|
||||
|
||||
void init_signals()
|
||||
{
|
||||
#ifdef linux
|
||||
usr2_act.sa_handler = (__sighandler_t) stop_wait;
|
||||
usr2_act.sa_flags = 0;
|
||||
usr2_act.sa_restorer =
|
||||
(void (*)()) (((unsigned int)(cstack) + sizeof(cstack) - 4) & ~3);
|
||||
wine_sigaction(SIGUSR2,&usr2_act,NULL);
|
||||
#endif
|
||||
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
usr2_act.sa_hadnler = (void (*)) stop_wait;
|
||||
usr2_act.sa_flags = SA_ONSTACK;
|
||||
usr2_act.sa_mask = sig_mask;
|
||||
usr2_act.sa_restorer =
|
||||
(void (*)()) (((unsigned int)(cstack) + sizeof(cstack) - 4) & ~3);
|
||||
if (sigaction(SIGUSR2,&usr2_act,NULL) <0) {
|
||||
perror("sigaction: SIGUSR2");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void ATOM_GlobalInit()
|
||||
{
|
||||
printf("ATOM_GlobalInit\n");
|
||||
}
|
||||
|
||||
|
||||
void idle_loop()
|
||||
{
|
||||
int timeout;
|
||||
for(timeout=500; timeout ; timeout--) {
|
||||
if (DDE_GetRemoteMessage())
|
||||
exit(0); ;
|
||||
usleep(1000);
|
||||
}
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void client()
|
||||
{
|
||||
MSG msg;
|
||||
msg.hwnd=(HWND)-1;
|
||||
msg.message= WM_DDE_INITIATE;
|
||||
msg.wParam= 3;
|
||||
msg.lParam= 4;
|
||||
if (!DDE_SendMessage(&msg))
|
||||
exit(-1);
|
||||
idle_loop();
|
||||
}
|
||||
void server()
|
||||
{
|
||||
DDE_IPC_init();
|
||||
idle_loop();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("Kill when done one message\n");
|
||||
init_signals();
|
||||
if (argc>1)
|
||||
server();
|
||||
else
|
||||
client();
|
||||
return 0;
|
||||
}
|
|
@ -1,683 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
|
||||
***************************************************************************
|
||||
* File: generic_hash.c
|
||||
* Purpose : dynamically growing hash, may use shared or local memory.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "generic_hash.h"
|
||||
|
||||
#define ROUND_UP4(num) (( (num)+3) & ~3)
|
||||
|
||||
#define FREE_ENTRY 0
|
||||
#define DELETED_ENTRY ((DWORD)-1)
|
||||
|
||||
#define NO_OF_PRIMES 512
|
||||
#define GET_ITEM(items,size,i)\
|
||||
(*(HASH_ITEM*) \
|
||||
( ((char *)(items))+ \
|
||||
(i)*(size)) )
|
||||
|
||||
static HASH_ITEM *locate_entry(HASH_CONTAINER* hash, DWORD key,
|
||||
HASH_VAL *seeked_data, BOOL skip_deleted);
|
||||
|
||||
static void copy_hash_items(HASH_CONTAINER *hash, HASH_ITEM *old_items,
|
||||
int old_n_items);
|
||||
|
||||
static BOOL arrays_initialized = FALSE;
|
||||
static int primes[NO_OF_PRIMES];
|
||||
static int best_primes[NO_OF_PRIMES];
|
||||
static int no_of_primes;
|
||||
static int no_of_best_primes;
|
||||
static int max_num;
|
||||
|
||||
/* binary search for `num' in the `primes' array */
|
||||
static BOOL prime_binary_search_found(int num)
|
||||
{
|
||||
int min_idx, max_idx, idx;
|
||||
|
||||
min_idx=0;
|
||||
max_idx=no_of_primes-1;
|
||||
|
||||
while (min_idx <= max_idx) {
|
||||
idx = (max_idx + min_idx) >> 1;
|
||||
if (num == primes[idx])
|
||||
return TRUE;
|
||||
if (num < primes[idx])
|
||||
max_idx = idx-1;
|
||||
else
|
||||
min_idx = idx+1;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL is_prime(int num)
|
||||
{
|
||||
int i;
|
||||
if ((num & 0x1) == 0) /* can be divided by 2 */
|
||||
if (num == 2)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
if (num <= primes[no_of_primes-1])
|
||||
return prime_binary_search_found(num);
|
||||
|
||||
for (i=0 ; i < no_of_primes ; i++) {
|
||||
if (num % primes[i] == 0)
|
||||
return FALSE;
|
||||
if (num < primes[i] * primes[i])
|
||||
return TRUE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void setup_primes()
|
||||
{
|
||||
int num;
|
||||
|
||||
primes[0]=2;
|
||||
primes[1]=3;
|
||||
no_of_primes=2;
|
||||
|
||||
/* count in modulo 6 to avoid numbers that divide by 2 or 3 */
|
||||
for (num=5 ; ; num+=6) {
|
||||
if (is_prime(num)) {
|
||||
primes[no_of_primes++]=num;
|
||||
if (no_of_primes >= NO_OF_PRIMES)
|
||||
break;
|
||||
}
|
||||
if (is_prime(num+2)) {
|
||||
primes[no_of_primes++]=num+2;
|
||||
if (no_of_primes >= NO_OF_PRIMES)
|
||||
break;
|
||||
}
|
||||
}
|
||||
max_num= primes[no_of_primes-1] * primes[no_of_primes-1];
|
||||
}
|
||||
|
||||
|
||||
/* Find primes which are far "enough" from powers of two */
|
||||
|
||||
void setup_best_primes()
|
||||
{
|
||||
int i;
|
||||
int num;
|
||||
int pow2before, pow2after;
|
||||
int min_range, max_range;
|
||||
|
||||
min_range=3;
|
||||
max_range=3;
|
||||
pow2before= 2;
|
||||
pow2after= 4;
|
||||
|
||||
no_of_best_primes= 0;
|
||||
for (i=0 ; i < no_of_primes ; i++){
|
||||
num= primes[i];
|
||||
|
||||
if (num > pow2after) {
|
||||
pow2before= pow2after;
|
||||
pow2after <<=1;
|
||||
min_range= pow2before+ (pow2before >> 3);
|
||||
max_range= pow2after- (pow2before >> 2);
|
||||
}
|
||||
if (num > min_range && num < max_range)
|
||||
best_primes[no_of_best_primes++]=num;
|
||||
}
|
||||
}
|
||||
|
||||
/* binary search for `num' in the `best_primes' array,
|
||||
* Return smallest best_prime >= num.
|
||||
*/
|
||||
static int best_prime_binary_search(int num)
|
||||
{
|
||||
int min_idx, max_idx, idx;
|
||||
|
||||
min_idx=0;
|
||||
max_idx=no_of_best_primes-1;
|
||||
|
||||
while (1) {
|
||||
idx = (max_idx + min_idx) >> 1;
|
||||
if (num == best_primes[idx])
|
||||
return num;
|
||||
if (num < best_primes[idx]) {
|
||||
max_idx = idx-1;
|
||||
if (max_idx <= min_idx)
|
||||
return best_primes[idx];
|
||||
}
|
||||
else {
|
||||
min_idx = idx+1;
|
||||
if (min_idx >= max_idx)
|
||||
return best_primes[max_idx];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Find the best prime, near `num' (which can be any number) */
|
||||
static int best_prime(int num)
|
||||
{
|
||||
int log2;
|
||||
int pow2less, pow2more;
|
||||
int min_range, max_range;
|
||||
|
||||
if (num < 11)
|
||||
return 11;
|
||||
|
||||
if (num <= best_primes[no_of_best_primes-1])
|
||||
return best_prime_binary_search(num);
|
||||
|
||||
assert( num < max_num );
|
||||
|
||||
for (log2=0 ; num >> log2 ; log2++)
|
||||
;
|
||||
|
||||
pow2less= 1 << log2;
|
||||
pow2more= 1 << (log2+1);
|
||||
min_range= pow2less + (pow2less >> 3);
|
||||
max_range= pow2more - (pow2more >> 3);
|
||||
|
||||
if (num < min_range)
|
||||
num= min_range;
|
||||
|
||||
num |= 1; /* make sure num can't be divided by 2 */
|
||||
|
||||
while (1) {
|
||||
if (num >= max_range) {
|
||||
pow2less<<= 1;
|
||||
pow2more<<= 1;
|
||||
min_range= pow2less + (pow2less >> 3);
|
||||
max_range= pow2more - (pow2more >> 3);
|
||||
num= min_range | 1; /* make sure num can't be divided by 2 */
|
||||
}
|
||||
/* num should be here in the range: (min_range, max_range) */
|
||||
if (is_prime(num))
|
||||
return num;
|
||||
num+=2;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: This can be done before compiling. (uning a script)*/
|
||||
static void setup_arrays()
|
||||
{
|
||||
setup_primes();
|
||||
setup_best_primes();
|
||||
}
|
||||
|
||||
/* Discard all DELETED_ENTRYs moving the data to it's correct location.
|
||||
* Done without a temporary buffer.
|
||||
* May require some efficiency improvements ( currently it's o(N^2)
|
||||
* or is it o(N^3) in the worst case ? In the avarege it seems to be
|
||||
* something like o(N log (N)))
|
||||
*/
|
||||
static void static_collect_garbge(HASH_CONTAINER *hash)
|
||||
{
|
||||
int i;
|
||||
BOOL change;
|
||||
HASH_ITEM *items;
|
||||
HASH_ITEM *located;
|
||||
HASH_ITEM *item;
|
||||
int key;
|
||||
|
||||
items= hash->items;
|
||||
|
||||
do {
|
||||
change= FALSE;
|
||||
for (i=hash->shared->total_items-1 ; i >= 0 ; i--) {
|
||||
item= &GET_ITEM(items,hash->bytes_per_item,i);
|
||||
key= item->key;
|
||||
if (key != DELETED_ENTRY && key != FREE_ENTRY) {
|
||||
/* try to place the entry in a deleted location */
|
||||
located= locate_entry(hash, key, &item->data,
|
||||
0 /* no skip_deleted */);
|
||||
if (located->key == DELETED_ENTRY) {
|
||||
change= TRUE;
|
||||
memcpy(&located, &item,
|
||||
hash->bytes_per_item);
|
||||
item->key= DELETED_ENTRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (change);
|
||||
|
||||
/* No change means that there is no need to go through a DELETED_ENTRY
|
||||
* in order to reach an item, so DELETED_ENTRY looses it's special
|
||||
* meaning, and it is the same as FREE_ENTRY.
|
||||
*/
|
||||
for (i=hash->shared->total_items-1 ; i >= 0 ; i--)
|
||||
if (GET_ITEM(items,hash->bytes_per_item,i).key == DELETED_ENTRY)
|
||||
GET_ITEM(items,hash->bytes_per_item,i).key = FREE_ENTRY;
|
||||
hash->shared->deleted_items=0;
|
||||
}
|
||||
|
||||
static void collect_garbge(HASH_CONTAINER *hash)
|
||||
{
|
||||
HASH_SHARED *shared= hash->shared;
|
||||
HASH_ITEM *temp_items;
|
||||
int size;
|
||||
|
||||
size= shared->total_items * hash->bytes_per_item;
|
||||
temp_items= (HASH_ITEM*)malloc(size);
|
||||
if (temp_items==NULL) {
|
||||
static_collect_garbge(hash);
|
||||
} else {
|
||||
memcpy(temp_items, hash->items, size);
|
||||
copy_hash_items(hash, temp_items, shared->total_items);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void copy_hash_items(HASH_CONTAINER *hash, HASH_ITEM *old_items,
|
||||
int old_n_items)
|
||||
{
|
||||
HASH_SHARED *shared= hash->shared;
|
||||
HASH_ITEM *item;
|
||||
int i;
|
||||
|
||||
shared->deleted_items = 0;
|
||||
shared->free_items= shared->total_items;
|
||||
|
||||
/* make all items free */
|
||||
for (i= shared->total_items-1 ; i>=0 ; i--)
|
||||
GET_ITEM(hash->items, hash->bytes_per_item, i).key = FREE_ENTRY;
|
||||
|
||||
/* copy items */
|
||||
for (i=0 ; i <= old_n_items; i++) {
|
||||
item= &GET_ITEM(old_items, hash->bytes_per_item,i);
|
||||
if (item->key != FREE_ENTRY && item->key != DELETED_ENTRY)
|
||||
hash_add_item(hash, item->key, &item->data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void reorder_hash(HASH_CONTAINER *hash)
|
||||
{
|
||||
HASH_SHARED *shared= hash->shared;
|
||||
HASH_ITEM *items, *old_items;
|
||||
HASH_PTR shared_items, old_shared_items;
|
||||
int n_items, old_n_items;
|
||||
int size;
|
||||
|
||||
if (shared->deleted_items > hash->min_free_items) {
|
||||
collect_garbge(hash);
|
||||
return;
|
||||
}
|
||||
n_items= best_prime(shared->total_items * HASH_REALLOC_JUMPS);
|
||||
|
||||
size= n_items *
|
||||
(sizeof(items[0]) - sizeof(items[0].data) + hash->bytes_per_item);
|
||||
|
||||
shared_items= hash->allocate_mem(size);
|
||||
items= hash->access_mem(shared_items);
|
||||
|
||||
if (items == NULL) {
|
||||
collect_garbge(hash);
|
||||
return;
|
||||
}
|
||||
old_shared_items = shared->items;
|
||||
old_n_items= shared->total_items;
|
||||
old_items= hash->items;
|
||||
|
||||
/* setup a new clean hash based on the parameters of the original one */
|
||||
hash->items= items;
|
||||
shared->total_items = n_items;
|
||||
shared->items= shared_items;
|
||||
set_hash_parameters(hash, hash->maximum_load);
|
||||
copy_hash_items(hash, old_items, old_n_items);
|
||||
|
||||
hash->free_mem(old_shared_items);
|
||||
hash->last_ptr_update= ++shared->ptr_updates;
|
||||
}
|
||||
|
||||
/* low level: attach hash existing hash items, no checks are performed
|
||||
* No complex calculations done.
|
||||
*/
|
||||
static HASH_CONTAINER *attach_no_check(HASH_ITEM *items, int bytes_per_datum)
|
||||
{
|
||||
HASH_CONTAINER *hash;
|
||||
int bytes_per_item;
|
||||
HASH_ITEM dummy_item;
|
||||
|
||||
hash= (HASH_CONTAINER*) malloc(sizeof(HASH_CONTAINER) );
|
||||
if (hash == NULL)
|
||||
return NULL;
|
||||
|
||||
bytes_per_item= bytes_per_datum+
|
||||
sizeof(dummy_item)-sizeof(dummy_item.data);
|
||||
hash->bytes_per_item= ROUND_UP4(bytes_per_item);
|
||||
hash->items= items;
|
||||
hash->is_correct_item= NULL;
|
||||
hash->allocate_mem= HASH_MEM_ALLOC;
|
||||
hash->access_mem= HASH_MEM_ACCESS;
|
||||
hash->free_mem= HASH_MEM_FREE;
|
||||
set_hash_parameters(hash, HASH_LOAD);
|
||||
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
/* Attach existing & running remote (i.e. shared) hash.
|
||||
* Attach the items using the data stored in "shared"
|
||||
*/
|
||||
HASH_CONTAINER *attach_remote_hash(HASH_SHARED *shared, int bytes_per_datum,
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR))
|
||||
{
|
||||
HASH_CONTAINER *hash;
|
||||
HASH_ITEM *items;
|
||||
|
||||
assert(access_mem != NULL);
|
||||
if (! arrays_initialized)
|
||||
setup_arrays();
|
||||
|
||||
items=access_mem(shared->items);
|
||||
hash= attach_no_check(items, bytes_per_datum);
|
||||
if (hash == NULL)
|
||||
return NULL;
|
||||
|
||||
hash->shared_was_malloced = FALSE;
|
||||
hash->shared= shared;
|
||||
return (hash);
|
||||
}
|
||||
|
||||
|
||||
HASH_CONTAINER *create_remote_hash(HASH_SHARED *shared,
|
||||
int bytes_per_datum,
|
||||
int total_items,
|
||||
HASH_PTR (*allocate_mem)(int size),
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR))
|
||||
{
|
||||
HASH_CONTAINER *hash;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
assert(total_items >= 1);
|
||||
assert(bytes_per_datum >=1);
|
||||
assert(access_mem != NULL);
|
||||
assert(allocate_mem != NULL);
|
||||
assert(shared != NULL);
|
||||
if (! arrays_initialized)
|
||||
setup_arrays();
|
||||
|
||||
if (total_items < MIN_HASH)
|
||||
total_items= MIN_HASH;
|
||||
else
|
||||
total_items= best_prime(total_items);
|
||||
|
||||
hash= attach_no_check(NULL, bytes_per_datum);
|
||||
|
||||
if (hash==NULL) {
|
||||
free(hash);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
shared->total_items= total_items;
|
||||
hash->shared= shared;
|
||||
hash->shared_was_malloced = FALSE;
|
||||
|
||||
size= total_items * hash->bytes_per_item;
|
||||
|
||||
shared->items = allocate_mem(size);
|
||||
hash->items= access_mem(shared->items);
|
||||
|
||||
if (hash->items == NULL ) {
|
||||
free(hash);
|
||||
return NULL;
|
||||
}
|
||||
shared->items.ptr= hash->items;
|
||||
|
||||
/* make all items free */
|
||||
for (i=0 ; i < total_items ; i++)
|
||||
GET_ITEM(hash->items,hash->bytes_per_item,i).key = FREE_ENTRY;
|
||||
|
||||
shared->deleted_items= 0;
|
||||
shared->free_items= total_items;
|
||||
shared->ptr_updates= 0;
|
||||
return hash;
|
||||
|
||||
}
|
||||
|
||||
/* hash constructor: create brand new hash */
|
||||
HASH_CONTAINER *create_hash(int bytes_per_datum, int total_items)
|
||||
{
|
||||
HASH_CONTAINER *hash;
|
||||
HASH_SHARED *shared;
|
||||
|
||||
|
||||
shared= (HASH_SHARED*)malloc(sizeof(HASH_SHARED));
|
||||
if (shared == NULL)
|
||||
return NULL;
|
||||
|
||||
hash= create_remote_hash(shared, bytes_per_datum, total_items,
|
||||
HASH_MEM_ALLOC, HASH_MEM_ACCESS);
|
||||
|
||||
if (hash == NULL) {
|
||||
free(shared);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hash->shared_was_malloced = TRUE;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* set the extra handlers to non default values */
|
||||
void set_hash_handlers(HASH_CONTAINER *hash,
|
||||
HASH_ITEM_TEST *is_correct_item,
|
||||
HASH_PTR (*allocate_mem)(int size),
|
||||
void (*free_mem)(HASH_PTR),
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR))
|
||||
{
|
||||
assert(hash);
|
||||
assert(allocate_mem);
|
||||
assert(free_mem);
|
||||
|
||||
hash->free_mem = free_mem;
|
||||
hash->allocate_mem = allocate_mem;
|
||||
hash->access_mem = access_mem;
|
||||
hash->is_correct_item = is_correct_item;
|
||||
}
|
||||
|
||||
|
||||
/* set extra parameters */
|
||||
void set_hash_parameters(HASH_CONTAINER *hash, int load)
|
||||
{
|
||||
assert(hash);
|
||||
assert(load>30); /* no sence to realloc with less than */
|
||||
/* 50% load, limiting to 30% to be on */
|
||||
/* the safe size */
|
||||
assert(load<=100);
|
||||
|
||||
hash->maximum_load= load;
|
||||
hash->min_free_items= (1.0 - load/100.0) * hash->shared->total_items + 1 ;
|
||||
}
|
||||
|
||||
/* hash destructor: destroy anything related to the hash */
|
||||
void destroy_hash(HASH_CONTAINER *hash)
|
||||
{
|
||||
assert(hash);
|
||||
hash->free_mem(hash->shared->items);
|
||||
if (hash->shared_was_malloced)
|
||||
free(hash->shared);
|
||||
free(hash);
|
||||
}
|
||||
|
||||
/* hash destructor: just detach hash, without destroing it (makes */
|
||||
/* sence in shared memory environment) */
|
||||
void detach_hash(HASH_CONTAINER *hash)
|
||||
{
|
||||
assert(hash);
|
||||
free(hash);
|
||||
}
|
||||
|
||||
|
||||
/********** Hash usage *************/
|
||||
static inline BOOL
|
||||
correct_entry(HASH_ITEM *item, int key, HASH_VAL *seeked_data,
|
||||
HASH_ITEM_TEST *is_correct_item, BOOL skip_deleted)
|
||||
{
|
||||
switch(item->key) {
|
||||
case FREE_ENTRY:
|
||||
return TRUE;
|
||||
|
||||
case DELETED_ENTRY:
|
||||
return skip_deleted ? FALSE : TRUE;
|
||||
|
||||
default:
|
||||
if (item->key != key)
|
||||
return FALSE;
|
||||
if (is_correct_item != NULL)
|
||||
return is_correct_item(&item->data, seeked_data);
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The algorithm of the hash (one of the 2 standard hash implementations):
|
||||
* Iterate through the hash table until
|
||||
* 1. The entry has been found.
|
||||
* 2. A FREE entry has been found.
|
||||
* 3. For insert operations only- A DELETED entry has been found.
|
||||
* The difference between DELETED and FREE entires is that
|
||||
* DELETED entry was one occupied, while FREE was never allocated.
|
||||
* The idea behind this structure to keep other entries reachable.
|
||||
*/
|
||||
|
||||
static HASH_ITEM *locate_entry(HASH_CONTAINER* hash, DWORD key,
|
||||
HASH_VAL *seeked_data, BOOL skip_deleted)
|
||||
{
|
||||
DWORD hash_idx, hash_leaps;
|
||||
HASH_ITEM *item;
|
||||
int i;
|
||||
int total_items;
|
||||
|
||||
assert(hash);
|
||||
|
||||
total_items= hash->shared->total_items;
|
||||
hash_idx= key % total_items;
|
||||
|
||||
item= &GET_ITEM(hash->items, hash->bytes_per_item, hash_idx);
|
||||
|
||||
if ( correct_entry( item, key, seeked_data,
|
||||
hash->is_correct_item, skip_deleted) )
|
||||
return item;
|
||||
|
||||
/* get the WORDs in different order in this DWORD to avoid clustering */
|
||||
hash_leaps=((DWORD)MAKELONG(HIWORD(key), LOWORD(key))
|
||||
% (total_items-1)) +1;
|
||||
|
||||
/* interate through the hash table using hash_leaps */
|
||||
for (i= total_items ; i ; i--) {
|
||||
hash_idx+= hash_leaps;
|
||||
if (hash_idx > total_items)
|
||||
hash_idx -= total_items;
|
||||
|
||||
item= &GET_ITEM(hash->items,hash->bytes_per_item, hash_idx);
|
||||
if ( correct_entry( item, key, seeked_data,
|
||||
hash->is_correct_item, skip_deleted) )
|
||||
return item;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
static inline void sync_shared_hash(HASH_CONTAINER *hash)
|
||||
{
|
||||
HASH_SHARED *shared= hash->shared;
|
||||
|
||||
if (shared->ptr_updates == hash->last_ptr_update)
|
||||
return;
|
||||
|
||||
assert(shared->ptr_updates >= hash->last_ptr_update);
|
||||
hash->last_ptr_update= shared->ptr_updates;
|
||||
hash->min_free_items= (1.0 - hash->maximum_load/100.0) *
|
||||
shared->total_items + 1 ;
|
||||
|
||||
hash->items= hash->access_mem(shared->items);
|
||||
}
|
||||
|
||||
HASH_VAL *hash_locate_item(HASH_CONTAINER* hash,
|
||||
int key, HASH_VAL *seeked_data)
|
||||
{
|
||||
HASH_ITEM *item;
|
||||
|
||||
assert(hash != NULL);
|
||||
sync_shared_hash(hash);
|
||||
|
||||
item= locate_entry(hash, key, seeked_data, 1 /* skip_deleted */);
|
||||
if (item == NULL)
|
||||
return NULL;
|
||||
if (item->key == FREE_ENTRY )
|
||||
return NULL;
|
||||
|
||||
return &item->data;
|
||||
}
|
||||
|
||||
|
||||
BOOL hash_add_item(HASH_CONTAINER* hash, int key, HASH_VAL *data)
|
||||
{
|
||||
HASH_SHARED *shared;
|
||||
HASH_ITEM *item;
|
||||
|
||||
assert(hash != NULL);
|
||||
|
||||
sync_shared_hash(hash);
|
||||
shared= hash->shared;
|
||||
|
||||
item=locate_entry(hash, key, data, 0 /* no skip_deleted */);
|
||||
assert(item != NULL);
|
||||
if (item->key == key)
|
||||
return FALSE;
|
||||
if (item->key == FREE_ENTRY)
|
||||
shared->free_items--;
|
||||
else
|
||||
shared->deleted_items--;
|
||||
|
||||
item->key= key;
|
||||
memcpy(&item->data, data, hash->bytes_per_item-sizeof(key));
|
||||
|
||||
if (shared->free_items < hash->min_free_items ||
|
||||
shared->deleted_items > hash->min_free_items)
|
||||
reorder_hash(hash);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
BOOL hash_delete_item(HASH_CONTAINER* hash, int key, HASH_VAL *seeked_data)
|
||||
{
|
||||
HASH_ITEM *item;
|
||||
|
||||
assert(hash != NULL);
|
||||
sync_shared_hash(hash);
|
||||
|
||||
item=locate_entry(hash, key, seeked_data, 1 /* skip_deleted */);
|
||||
if (item == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (item->key == FREE_ENTRY)
|
||||
return FALSE;
|
||||
|
||||
item->key = DELETED_ENTRY;
|
||||
hash->shared->deleted_items++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void *ret_null()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
HASH_ITEM *access_local_hash(HASH_PTR ptr)
|
||||
{
|
||||
return ptr.ptr;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,142 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
|
||||
***************************************************************************
|
||||
* File: generic_hash.h
|
||||
* Purpose : dynamically growing hash, may use shared or local memory.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifndef _GENERIC_HASH_H_
|
||||
#define _GENERIC_HASH_H_
|
||||
|
||||
#include "windef.h"
|
||||
#include "shm_block.h"
|
||||
#include "win.h"
|
||||
/* default hash values */
|
||||
#define HASH_LOAD 70
|
||||
#define HASH_MEM_ALLOC (HASH_PTR (*)(int size)) malloc
|
||||
#define HASH_MEM_FREE (void (*)(HASH_PTR)) free
|
||||
#define HASH_MEM_ACCESS access_local_hash
|
||||
#define HASH_REALLOC_JUMPS 1.5 /* Relative size of the new memory */
|
||||
#define MIN_HASH 13
|
||||
|
||||
typedef union {
|
||||
char string[1];
|
||||
WORD words[1];
|
||||
DWORD dwords[1];
|
||||
char *ptr;
|
||||
SEGPTR segptr;
|
||||
} HASH_VAL;
|
||||
|
||||
typedef struct hash_item_struct {
|
||||
DWORD key;
|
||||
HASH_VAL data;
|
||||
} HASH_ITEM;
|
||||
|
||||
/* point to the hash structure */
|
||||
typedef union {
|
||||
HASH_ITEM* ptr; /* Local pointer */
|
||||
REL_PTR rel; /* IPC relative address */
|
||||
SEGPTR segptr; /* Universal (can be IPC or local) */
|
||||
} HASH_PTR;
|
||||
|
||||
typedef struct hash_share_struct {
|
||||
int total_items; /* total number of items (array size) */
|
||||
int free_items; /* number of free items (excluding deleted) */
|
||||
int deleted_items; /* number of deleted items */
|
||||
int ptr_updates; /* Number of updates to `items' pointer */
|
||||
/* (of items) - used for intecepting */
|
||||
/* changes to the pointer. */
|
||||
HASH_PTR items; /* pointer to the items */
|
||||
} HASH_SHARED;
|
||||
typedef BOOL HASH_ITEM_TEST(HASH_VAL *value, HASH_VAL *seeked_data);
|
||||
|
||||
/* NOTE:
|
||||
* 1. Keys 0 and -1 are reserved.
|
||||
* 2. none of these items should be accessed directly, use existing
|
||||
* functions. If they are not enough, add a new function.
|
||||
*/
|
||||
typedef struct hash_container_struct {
|
||||
int bytes_per_item;
|
||||
int maximum_load; /* in percents (0..100) default is 70 */
|
||||
int min_free_items; /* minimum free items before reallocating
|
||||
(Function of maximum_load) */
|
||||
|
||||
int last_ptr_update; /* to be compared with shared.ptr_updates */
|
||||
BOOL shared_was_malloced; /* Need that to know how to destroy hash */
|
||||
|
||||
/* This is an optional handler.
|
||||
* If not NULL, this function is used for distinguishing between
|
||||
* different data with the same key (key field holds integer and
|
||||
* is too short for long keys like strings).
|
||||
*/
|
||||
HASH_ITEM_TEST *is_correct_item;
|
||||
|
||||
/* Handlers used for reallocating memory
|
||||
* [by allocating new data and then freeing old data]
|
||||
*/
|
||||
HASH_PTR (*allocate_mem)(int size);
|
||||
void (*free_mem)(HASH_PTR);
|
||||
|
||||
/* Translator from HASH_PTR construct to a regular pointer.
|
||||
use HASH_MEM_ACCESS, if no translation is needed */
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR);
|
||||
|
||||
HASH_ITEM *items;
|
||||
HASH_SHARED *shared; /* Things to be on shared memory. */
|
||||
} HASH_CONTAINER;
|
||||
|
||||
|
||||
/********** Hash maintenance functions ***********/
|
||||
|
||||
|
||||
|
||||
/* Attach existing & running remote (i.e. shared) hash.
|
||||
* Attach the items using the data stored in "shared"
|
||||
*/
|
||||
HASH_CONTAINER *attach_remote_hash(HASH_SHARED *shared, int bytes_per_datum,
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR));
|
||||
|
||||
|
||||
HASH_CONTAINER *create_remote_hash(HASH_SHARED *shared,
|
||||
int bytes_per_datum,
|
||||
int total_items,
|
||||
HASH_PTR (*allocate_mem)(int size),
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR));
|
||||
/* hash constructor: create brand new hash (not on shared memory) */
|
||||
HASH_CONTAINER *create_hash(int bytes_per_datum, int total_items);
|
||||
|
||||
/* set the extra handlers to non default values */
|
||||
void set_hash_handlers(HASH_CONTAINER *hash,
|
||||
HASH_ITEM_TEST *is_correct_item,
|
||||
HASH_PTR (*allocate_mem)(int size),
|
||||
void (*free_mem)(HASH_PTR),
|
||||
HASH_ITEM *(*access_mem)(HASH_PTR));
|
||||
|
||||
/* set extra parameters */
|
||||
void set_hash_parameters(HASH_CONTAINER *hash, int load);
|
||||
|
||||
/* hash destructors */
|
||||
void destroy_hash(HASH_CONTAINER *hash);
|
||||
void detach_hash(HASH_CONTAINER *hash);
|
||||
|
||||
|
||||
/********** Hash usage *************/
|
||||
|
||||
/* All following functions have the same format:
|
||||
* hash- the hash structure to use
|
||||
* key- used as primary means to get to the entry.
|
||||
* data- 1. a secondary key (used only if `is_correct_item' is set).
|
||||
* 2. data to store. (for hash_add_item).
|
||||
*/
|
||||
HASH_VAL *hash_locate_item(HASH_CONTAINER* hash,int key, HASH_VAL* seeked_data);
|
||||
BOOL hash_add_item(HASH_CONTAINER* hash, int key, HASH_VAL* data);
|
||||
BOOL hash_delete_item(HASH_CONTAINER* hash, int key, HASH_VAL* seeked_data);
|
||||
|
||||
|
||||
void *ret_null(); /* function returning null (used for */
|
||||
/* disabling memory reallocation) */
|
||||
|
||||
/* access function used by local (non IPC) memory */
|
||||
HASH_ITEM *access_local_hash(HASH_PTR ptr);
|
||||
|
||||
#endif /* _GENERIC_HASH_H_ */
|
117
ipc/hash_test.c
117
ipc/hash_test.c
|
@ -1,117 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
|
||||
***************************************************************************
|
||||
* File: hash_test.c
|
||||
* Purpose : test generic_hash correctness.
|
||||
* NOTE:
|
||||
* This code covers only about 80% of generic_hash code.
|
||||
* There might be bugs in the remaining 20% - although most
|
||||
* of the functionality is tested with wine linckage.
|
||||
* For complete testing a little more work should be done.
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "generic_hash.h"
|
||||
|
||||
#define SIZE 200
|
||||
typedef struct { int a,b;} DATA ;
|
||||
DATA data[SIZE];
|
||||
int keys[SIZE];
|
||||
int peeks=0;
|
||||
|
||||
HASH_CONTAINER *hash1;
|
||||
HASH_CONTAINER *hash2; /* actual data is shared with hash1 */
|
||||
|
||||
/* test insertion using keys[] and data[] inserting using hash1 and */
|
||||
/* hash2 periodically, test hash after every 2 insertions */
|
||||
void test_insert()
|
||||
{
|
||||
int i,j;
|
||||
HASH_VAL *item;
|
||||
|
||||
printf("testing insertion \n");
|
||||
for (i=0 ; i < SIZE-1 ; i+=2) {
|
||||
assert(hash_add_item(hash1, keys[i], (HASH_VAL *)&data[i]));
|
||||
assert(hash_add_item(hash2, keys[i+1], (HASH_VAL *)&data[i+1]));
|
||||
for (j=0 ; j <= i+1 ; j++) {
|
||||
item= hash_locate_item(hash1, keys[j], (HASH_VAL *)&data[j]);
|
||||
if (item == NULL) {
|
||||
printf("NULL item: i=%d,j=%d\n",i,j);
|
||||
continue;
|
||||
}
|
||||
peeks++;
|
||||
if (memcmp(item,&data[j],sizeof(DATA))!=0) {
|
||||
printf("i=%d,j=%d\n",i,j);
|
||||
printf("saved=(%d,%d), orig=(%d,%d)\n",
|
||||
((DATA*)item)->a, ((DATA*)item)->b,
|
||||
data[j].a, data[j].b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* test deletion using keys[] and data[] deleting using hash1 and */
|
||||
/* hash2 periodicly, test hash after every 2 deletions */
|
||||
void test_delete()
|
||||
{
|
||||
int i,j;
|
||||
HASH_VAL *item;
|
||||
|
||||
printf("testing deletion\n");
|
||||
for (i=0 ; i < SIZE-1 ; i+=2) {
|
||||
assert(hash_delete_item(hash2, keys[i], NULL));
|
||||
assert(hash_delete_item(hash1, keys[i+1], NULL));
|
||||
for (j=0 ; j < SIZE ; j++) {
|
||||
item= hash_locate_item(hash2, keys[j], (HASH_VAL *)&data[j]);
|
||||
if (item == NULL) {
|
||||
if ( j > i+1)
|
||||
printf("NULL item: i=%d,j=%d\n",i,j);
|
||||
continue;
|
||||
}
|
||||
if (item != NULL && j <= i+1) {
|
||||
printf("Non NULL item: i=%d,j=%d\n",i,j);
|
||||
continue;
|
||||
}
|
||||
if (memcmp(item,&data[j],sizeof(DATA))!=0) {
|
||||
printf("i=%d,j=%d\n",i,j);
|
||||
printf("saved=(%d,%d), orig=(%d,%d)\n",
|
||||
((DATA*)item)->a, ((DATA*)item)->b,
|
||||
data[j].a, data[j].b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
|
||||
hash1= create_hash(sizeof(DATA), 1);
|
||||
assert(hash1);
|
||||
hash2= attach_remote_hash(hash1->shared, sizeof(DATA), HASH_MEM_ACCESS);
|
||||
assert(hash2);
|
||||
|
||||
for (i=0 ; i< SIZE ; i++) {
|
||||
data[i].a= rand();
|
||||
data[i].b= rand();
|
||||
keys[i]= rand();
|
||||
}
|
||||
|
||||
test_insert();
|
||||
detach_hash(hash1);
|
||||
free(hash1);
|
||||
hash1= attach_remote_hash(hash2->shared, sizeof(DATA), HASH_MEM_ACCESS);
|
||||
|
||||
test_delete();
|
||||
test_insert();
|
||||
|
||||
detach_hash(hash1);
|
||||
destroy_hash(hash2);
|
||||
printf("peeks=%d\n", peeks);
|
||||
return 0;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
bit_array_test
|
||||
bit_array=$?
|
||||
|
||||
dde_mem_test
|
||||
mem=$?
|
||||
|
||||
hash_test
|
||||
hash=$?
|
||||
|
||||
shm_semaph_test
|
||||
semaph=$?
|
||||
|
||||
dde_atom_test
|
||||
atom=$?
|
||||
|
||||
dde_proc_test 1 > proc_server &
|
||||
sleep 1
|
||||
dde_proc_test > proc_client
|
||||
fgrep "DDE:receive sent message. msg=03e0 wPar=fffb lPar=00000004" proc_server &&
|
||||
fgrep "DDE_GetRemoteMessage: sending ACK to wnd=fffb, proc=1" proc_server &&
|
||||
fgrep "get_ack: received DDE_ACK message" proc_client
|
||||
proc=$?
|
||||
rm proc_client proc_server
|
||||
|
||||
shm_fragment_test | diff TEST_FRAGMENT.std -
|
||||
fragment=$?
|
||||
|
||||
echo ====================================================================
|
||||
echo Test results:
|
||||
|
||||
echo -n "bit_array "
|
||||
if [ $bit_array -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
|
||||
|
||||
echo -n "dde_mem "
|
||||
if [ $mem -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
|
||||
|
||||
echo -n "hash "
|
||||
if [ $hash -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
|
||||
|
||||
echo -n "shm_semaph "
|
||||
if [ $semaph -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
|
||||
|
||||
echo -n "dde_proc "
|
||||
if [ $proc -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
|
||||
|
||||
echo -n "shm_fragment "
|
||||
if [ $fragment -eq 0 ] ; then echo OK ; else echo "** ERROR **" ; fi
|
194
ipc/shm_block.c
194
ipc/shm_block.c
|
@ -1,194 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_block.c
|
||||
* Purpose: Treat a shared memory block.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/sem.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "debugtools.h"
|
||||
#include "global.h"
|
||||
#include "selectors.h"
|
||||
#include "shm_fragment.h"
|
||||
#include "shm_block.h"
|
||||
#include "shm_semaph.h"
|
||||
#include "dde_proc.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(shm)
|
||||
|
||||
/* How each shmid is maped to local pointer */
|
||||
/* Only attached shm blocks are in this construct */
|
||||
struct local_shm_map *shm_map=NULL;
|
||||
|
||||
/* setup a new shm block (construct a shm block object).
|
||||
* block: The pointer to the memory block (local mapping)
|
||||
* first: The first data byte (excluding header stuff),
|
||||
* if 0 (zero) Use the default.
|
||||
* size: The size of the memory block.
|
||||
*/
|
||||
void shm_setup_block(struct shm_block *block, int first, int size)
|
||||
{
|
||||
TRACE("Setting up shm block at 0x%08x\n",(int )block);
|
||||
/* setup block internal data structure */
|
||||
if (first <= 0) {
|
||||
first=sizeof(*block);
|
||||
/* round up - so everything starts on cache line boundary
|
||||
* (assume cache line=32 bytes, may be bigger/smaller for
|
||||
* different processors and different L2 caches .)
|
||||
*/
|
||||
first=(first+0x1f) & ~0x1f;
|
||||
}
|
||||
block->free=size-first;
|
||||
block->next_shm_id=-1; /* IPC shm ID (for initial linking) */
|
||||
block->proc_idx= curr_proc_idx;
|
||||
/* block->size is initialized in shm_FragmentInit */
|
||||
shm_FragmentInit(block, first, size); /* first item in the free list */
|
||||
|
||||
TRACE("block was set up at 0x%08x, size=0x%04xKB, 1st usable=%02x\n",
|
||||
(int )block,size/1024,first);
|
||||
}
|
||||
|
||||
/* shm_attach_block: attach existing shm block, setup selectors
|
||||
* shm_id - id of the block to attach.
|
||||
* proc_idx - if not -1, puts this data into local mapping
|
||||
* map - localy mapped info about this block.
|
||||
*/
|
||||
/* NOTE: there is no check if this block is already attached.
|
||||
* Attaching the same block more than once - is possible
|
||||
* In case of doubt use shm_locate_block.
|
||||
*/
|
||||
struct shm_block *shm_attach_block(int shm_id, int proc_idx,
|
||||
struct local_shm_map *map)
|
||||
{
|
||||
struct shm_block *block;
|
||||
struct shmid_ds ds;
|
||||
struct local_shm_map *this;
|
||||
|
||||
shmctl(shm_id, IPC_STAT, &ds );
|
||||
|
||||
block=(struct shm_block*)shmat(shm_id, NULL, 0);
|
||||
if (block==NULL || block == (struct shm_block*) -1) return NULL;
|
||||
|
||||
this=(struct local_shm_map *)xmalloc(sizeof(*this));
|
||||
this->next= shm_map;
|
||||
shm_map = this;
|
||||
this->shm_id= shm_id;
|
||||
this->ptr = block;
|
||||
|
||||
if (proc_idx < 0)
|
||||
this->proc_idx=block->proc_idx;
|
||||
else
|
||||
this->proc_idx=proc_idx;
|
||||
|
||||
if (map != NULL) {
|
||||
memcpy(map, this, sizeof(map));
|
||||
map->next= NULL; /* don't pass private info */
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
struct shm_block *shm_create_block(int first, int size, int *shm_id)
|
||||
{
|
||||
struct shm_block *block;
|
||||
|
||||
if (size==0)
|
||||
size=SHM_MINBLOCK;
|
||||
else
|
||||
/* round up size to a multiple of SHM_MINBLOCK */
|
||||
size= (size+SHM_MINBLOCK-1) & ~(SHM_MINBLOCK-1);
|
||||
*shm_id= shmget ( IPC_PRIVATE, size ,0700);
|
||||
if (*shm_id==-1)
|
||||
return NULL;
|
||||
block=shm_attach_block(*shm_id, curr_proc_idx, NULL);
|
||||
if (block!=NULL)
|
||||
shm_setup_block(block, first, size);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/*
|
||||
** Locate attached block. (return it, or NULL on failure)
|
||||
** shm_id is the block we look for.
|
||||
** *map - will get all the info related to this local map + proc_idx
|
||||
** (may be NULL)
|
||||
** *seg - will get the segment this block is attached to.
|
||||
*/
|
||||
struct shm_block *shm_locate_attached_block(int shm_id,
|
||||
struct local_shm_map *map)
|
||||
{
|
||||
struct local_shm_map *curr;
|
||||
|
||||
for (curr= shm_map ; curr != NULL ; curr= curr->next) {
|
||||
if (curr->shm_id == shm_id) {
|
||||
if (map) {
|
||||
memcpy(map, curr, sizeof(*curr) );
|
||||
map->next = NULL; /* this is private info ! */
|
||||
}
|
||||
return curr->ptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* block not found ! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* shm_locate_block: see shm_attach_block.
|
||||
In addition to shm_attach_block, make sure this
|
||||
block is not already attached.
|
||||
*/
|
||||
struct shm_block *shm_locate_block(int shm_id, struct local_shm_map *map)
|
||||
{
|
||||
|
||||
struct shm_block *ret;
|
||||
ret= shm_locate_attached_block(shm_id, map);
|
||||
if (ret!=NULL)
|
||||
return ret;
|
||||
/* block not found ! , try to attach */
|
||||
return shm_attach_block(shm_id, -1, map);
|
||||
}
|
||||
|
||||
static void forget_attached(int shmid)
|
||||
{
|
||||
struct local_shm_map *curr, **point_to_curr;
|
||||
|
||||
for (curr= shm_map, point_to_curr= &shm_map ;
|
||||
curr != NULL ;
|
||||
curr= curr->next, point_to_curr= &curr->next ) {
|
||||
if (curr->shm_id == shmid) {
|
||||
*point_to_curr= curr->next;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* delete chain of shm blocks (pointing to each other)
|
||||
* Do it in reverse order. (This is what the recursion is for)
|
||||
*/
|
||||
void shm_delete_chain(int *shmid)
|
||||
{
|
||||
struct shm_block *block;
|
||||
|
||||
if (*shmid == -1)
|
||||
return;
|
||||
|
||||
block= shm_locate_block(*shmid, NULL);
|
||||
forget_attached( *shmid );
|
||||
if (block == NULL)
|
||||
return;
|
||||
shm_delete_chain(&block->next_shm_id);
|
||||
shmctl(*shmid, IPC_RMID, NULL);
|
||||
*shmid=-1;
|
||||
shmdt((char *)block);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,181 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_fragment.c
|
||||
* Purpose: Data fragments and free list items. Allocate and free blocks.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <stdio.h> /* for debugging only */
|
||||
#include "debugtools.h" /* for "stddeb" */
|
||||
|
||||
#include "shm_fragment.h"
|
||||
#include "shm_block.h"
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Free list: all fragments are ordered according to memory location.
|
||||
* new fragments are inserted in this way.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#define FRAG_PTR(block,ofs) ((struct shm_fragment *) ((char *) block + ofs) )
|
||||
#define NEXT_FRAG(block,ofs) ( FRAG_PTR(block,ofs)->info.next )
|
||||
|
||||
/* setup first item in the free list */
|
||||
void shm_FragmentInit(struct shm_block *block,int first, int size)
|
||||
{
|
||||
struct shm_fragment *fragment;
|
||||
|
||||
/* round up to nearest 16 byte boundary */
|
||||
first=(first+15)& ~15;
|
||||
block->free_list=first;
|
||||
|
||||
/* make all the block (exluding the header) free */
|
||||
fragment= FRAG_PTR(block, first);
|
||||
block->free= fragment->size= size-first;
|
||||
fragment->info.next=0;
|
||||
}
|
||||
|
||||
void shm_FragPtrFree(struct shm_block *block, void *ptr)
|
||||
{
|
||||
/* ptr points to fragment->info.data, find pointer to fragment,
|
||||
* find the offset of this pointer in block.
|
||||
*/
|
||||
if (ptr)
|
||||
shm_FragmentFree(block, PTR2REL(block, ptr));
|
||||
}
|
||||
void shm_FragmentFree(struct shm_block *block, int fragment_ofs)
|
||||
{
|
||||
struct shm_fragment *fragment=NULL;
|
||||
int prev;
|
||||
int next;
|
||||
|
||||
fragment_ofs-=(int )&fragment->info.data;
|
||||
fragment= FRAG_PTR(block, fragment_ofs);
|
||||
|
||||
block->free+=fragment->size;
|
||||
/* scan free list to find candidates for merging with fragment */
|
||||
for (prev=0, next=block->free_list;
|
||||
(next!=0) && (fragment_ofs > next) ;
|
||||
prev=next, next=NEXT_FRAG(block,next) )
|
||||
;
|
||||
|
||||
/* insert fragment between, prev and next
|
||||
* prev==0: fragment will be the first item in free list
|
||||
* next==0: fragment will be the last item in free list
|
||||
*/
|
||||
|
||||
|
||||
/* update fragment (point to next, or merge with next) */
|
||||
|
||||
if ( fragment_ofs+fragment->size == next ) {
|
||||
/* merge with the next free block */
|
||||
fragment->size+= FRAG_PTR(block,next)->size;
|
||||
fragment->info.next=FRAG_PTR(block,next)->info.next;
|
||||
} else
|
||||
/* fragment should be inserted before the next fragment or end of */
|
||||
/* list. (not merged) */
|
||||
fragment->info.next=next;
|
||||
/* now fragment has all the information about the rest of the list */
|
||||
|
||||
|
||||
/* upate prev fragment (point or merge with fragment) */
|
||||
|
||||
if (prev==0) /* first item in free list */
|
||||
block->free_list=fragment_ofs;
|
||||
else if ( prev+FRAG_PTR(block,prev)->size == fragment_ofs ) {
|
||||
/* merge fragment with previous fragment */
|
||||
FRAG_PTR(block,prev)->size+= fragment->size;
|
||||
FRAG_PTR(block,prev)->info.next=fragment->info.next;
|
||||
} else
|
||||
/* insert fragment after previous fragment */
|
||||
FRAG_PTR(block,prev)->info.next=fragment_ofs;
|
||||
}
|
||||
|
||||
/* use "first fit" algorithm,
|
||||
* return: offset to data in fragment.
|
||||
*/
|
||||
int shm_FragmentAlloc(struct shm_block *block, int size)
|
||||
{
|
||||
int prev;
|
||||
int candidate;
|
||||
struct shm_fragment *fragment;
|
||||
struct shm_fragment *ret_fragment;
|
||||
|
||||
if (size <= 0)
|
||||
return NIL;
|
||||
/* add size of "fragment->size" */
|
||||
size+= (char *)&fragment->info.data - (char *)fragment ;
|
||||
|
||||
/* round "size" to nearest 16 byte value */
|
||||
size= (size+15) & ~15;
|
||||
if (size > block->free)
|
||||
return NIL;
|
||||
/* scan free list to find candidates for allocation */
|
||||
for (prev=0, candidate=block->free_list;
|
||||
candidate!=0 ;
|
||||
prev=candidate, candidate= fragment->info.next )
|
||||
{
|
||||
fragment=FRAG_PTR(block,candidate);
|
||||
if (fragment->size >= size)
|
||||
break;
|
||||
}
|
||||
|
||||
if (candidate == 0)
|
||||
return NIL;
|
||||
|
||||
block->free-=size;
|
||||
if (fragment->size == size) {
|
||||
if (prev == 0)
|
||||
block->free_list= fragment->info.next;
|
||||
else
|
||||
FRAG_PTR(block,prev)->info.next= fragment->info.next;
|
||||
return PTR2REL(block, &fragment->info.data);
|
||||
}
|
||||
|
||||
/* fragment->size > size */
|
||||
|
||||
/* Split fragment in two, return one part, put the other in free list. */
|
||||
/* The part that starts at the old location - will stay in the free list. */
|
||||
fragment->size -= size;
|
||||
|
||||
ret_fragment=FRAG_PTR(block, candidate + fragment->size);
|
||||
ret_fragment->size= size;
|
||||
return PTR2REL(block, ret_fragment->info.data);
|
||||
}
|
||||
|
||||
/* like shm_FragmentAlloc, returns pointer instead of offset */
|
||||
char *shm_FragPtrAlloc(struct shm_block *block, int size)
|
||||
{
|
||||
int ofs;
|
||||
ofs= shm_FragmentAlloc(block,size);
|
||||
if (ofs == NIL)
|
||||
return NULL;
|
||||
else
|
||||
return (char *) REL2PTR(block, ofs);
|
||||
}
|
||||
/* This is used for debugging only */
|
||||
void shm_print_free_list(struct shm_block *block)
|
||||
{
|
||||
struct shm_fragment *fragment;
|
||||
int item;
|
||||
|
||||
item=block->free_list;
|
||||
if (item==0) {
|
||||
DUMP("no free fragments");
|
||||
} else {
|
||||
for (; item ; item=fragment->info.next) {
|
||||
fragment=FRAG_PTR(block,item);
|
||||
DUMP("{0x%04x,0x%04x} ",item,fragment->size);
|
||||
}
|
||||
}
|
||||
DUMP(" [total free=%04x]\n",block->free);
|
||||
fflush(stddeb);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,99 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_fragment_test.c
|
||||
* Purpose: Test data fragments and free list items. Allocate and free blocks.
|
||||
***************************************************************************
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#define DEBUG_DEFINE_VARIABLES /* just avoid dumb errors */
|
||||
#include "debugtools.h" /* for "stddeb" */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "shm_block.h"
|
||||
#include "shm_fragment.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
#define DO_FREE(id) (-id)
|
||||
#define LIST_LENGTH 20
|
||||
|
||||
int main()
|
||||
{
|
||||
struct shm_block *block;
|
||||
char *ret;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
/* important: The test will work only for the current implementation of */
|
||||
/* allocation, if the implementation will change, the list should also */
|
||||
/* cahnge. */
|
||||
static int sizes[LIST_LENGTH]={
|
||||
SHM_MINBLOCK, /* 0: should fail */
|
||||
0x3fe0-4, /* 1: */
|
||||
0x4000-4, /* 2: */
|
||||
0x4000-4, /* 3: */
|
||||
0x4000-4+1, /* 4: should fail */
|
||||
0x4000-4, /* 5: */
|
||||
/* allocated(5,3,2,1) free() */
|
||||
-5, /* 6: */
|
||||
0x1c00-4, /* 7: */
|
||||
0x1400-4, /* 8: */
|
||||
0x1000-4, /* 9: */
|
||||
/* allocated(9,8,7,3,2,1) free() */
|
||||
-9, /* 10: */
|
||||
-3, /* 11: */
|
||||
-1, /* 12: */
|
||||
/* allocated(8,7,2) free(9,3,1) */
|
||||
0x1000-4, /* 13: */
|
||||
-13, /* 14: */
|
||||
0x1000+1-4, /* 15: */
|
||||
/* allocated(8,7,15,2) free(9,[3-15],1) */
|
||||
-2, /* 16: */
|
||||
/* allocated(8,7,15) free(9,[3-15],1+2) */
|
||||
-8, /* 17: */
|
||||
-7, /* 18: */
|
||||
-15 /* 19: */
|
||||
};
|
||||
|
||||
static char *ptr[LIST_LENGTH];
|
||||
|
||||
block=xmalloc(SHM_MINBLOCK);
|
||||
|
||||
/* setup first item in the free list */
|
||||
shm_FragmentInit(block, sizeof(*block), SHM_MINBLOCK);
|
||||
|
||||
fprintf(stddeb,"After shm_FragmentInit\n");
|
||||
shm_print_free_list(block);
|
||||
|
||||
for(i=0 ; i < LIST_LENGTH; i++) {
|
||||
size=sizes[i];
|
||||
if (size>0) { /* allocate */
|
||||
ret=shm_FragPtrAlloc(block, size);
|
||||
ptr[i]=ret;
|
||||
fprintf(stddeb,
|
||||
"%d: After shm_FragmentAlloc(block, 0x%06x) == ",
|
||||
i, size);
|
||||
if (ret==NULL)
|
||||
fprintf(stddeb, "NULL\n");
|
||||
else {
|
||||
fprintf(stddeb, "0x%06x\n", (int)ret-(int)block);
|
||||
memset( ret,0, size ); /* test boundaries */
|
||||
}
|
||||
} else { /* free */
|
||||
/* free shm fragment */
|
||||
ret=ptr[-sizes[i]];
|
||||
fprintf(stddeb, "%d: Doing shm_FragmentFree(block, ", i);
|
||||
if (ret==NULL)
|
||||
fprintf(stddeb, "NULL)\n");
|
||||
else
|
||||
fprintf(stddeb, "0x%06x)\n", (int)ret-(int)block);
|
||||
fflush(stddeb);
|
||||
shm_FragPtrFree(block, ret);
|
||||
}
|
||||
shm_print_free_list(block);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_main_blk.c
|
||||
* Purpose: Main Wine's shared memory block
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/sem.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include "debugtools.h"
|
||||
#include "shm_fragment.h"
|
||||
#include "shm_block.h"
|
||||
#include "shm_main_blk.h"
|
||||
#include "shm_semaph.h"
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(shm)
|
||||
|
||||
#define WineKey ( 'W'+((int)'i'<<8)+((int)'n'<<16)+((int)'e'<<24) )
|
||||
#define SHM_KEY_RANGE 8
|
||||
|
||||
/* main block (set during initialization) */
|
||||
struct shm_main_block *main_block=NULL;
|
||||
static char *shm_header="Wine - Windows emulator DDE mechanism";
|
||||
static int main_shm_id;
|
||||
|
||||
static void shm_main_refresh();
|
||||
|
||||
/* for debugging only */
|
||||
static void print_perm(struct ipc_perm *perm)
|
||||
{
|
||||
printf("Permission:\n");
|
||||
/* FIXME: not portable
|
||||
printf("\tKey=%d, mode=%03o, sequence #=%d\n",
|
||||
(int)perm->key,perm->mode, perm->seq);
|
||||
*/
|
||||
printf("\towner: uid=%d, gid=%d ;" ,perm->uid, perm->gid);
|
||||
printf(" creator: uid=%d, gid=%d\n",perm->cuid,perm->cgid);
|
||||
}
|
||||
|
||||
/* for debugging only */
|
||||
/* print_shm_info: print shared memory descriptor info */
|
||||
static void print_shm_info(int shm_id)
|
||||
{
|
||||
struct shmid_ds ds;
|
||||
shmctl(shm_id, IPC_STAT, &ds );
|
||||
|
||||
printf("shm_id=%d, Size=0x%08x , Number of attaches=%d\n",
|
||||
shm_id, ds.shm_segsz, (int)ds.shm_nattch);
|
||||
if (ds.shm_atime)
|
||||
printf("Last attach=%s",ctime(&ds.shm_atime));
|
||||
if (ds.shm_dtime)
|
||||
printf("Last detach=%s",ctime(&ds.shm_dtime));
|
||||
printf("Last change=%s",ctime(&ds.shm_ctime));
|
||||
printf("pid: creator=%d, last operator=%d\n",
|
||||
(int)ds.shm_cpid,(int)ds.shm_lpid);
|
||||
print_perm( &ds.shm_perm);
|
||||
|
||||
}
|
||||
|
||||
int proc_exist(pid_t pid)
|
||||
{
|
||||
if ( kill(pid,0) == 0) /* dummy signal to test existence */
|
||||
return 1;
|
||||
else if (errno==ESRCH) /* "no such process" */
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* setup a new main shm block (only construct a shm block object). */
|
||||
static void shm_setup_main_block()
|
||||
{
|
||||
TRACE("creating data structure\n");
|
||||
main_block->build_lock=1;
|
||||
strcpy(main_block->magic, shm_header);
|
||||
|
||||
shm_setup_block(&main_block->block,sizeof(*main_block),SHM_MINBLOCK);
|
||||
|
||||
dde_proc_init(main_block->proc);
|
||||
ATOM_GlobalInit();
|
||||
shm_sem_init(&main_block->sem);
|
||||
|
||||
/* main block set and data structure is stable now */
|
||||
main_block->build_lock=0;
|
||||
}
|
||||
|
||||
/* Delete everything related to main_block */
|
||||
void shm_delete_all(int shmid)
|
||||
{
|
||||
int proc_idx;
|
||||
|
||||
if (shmid == -1)
|
||||
shmid= main_shm_id;
|
||||
|
||||
shmctl( shmid, IPC_RMID, NULL);
|
||||
|
||||
for (proc_idx= 0 ; proc_idx < DDE_PROCS ; proc_idx++)
|
||||
dde_proc_done( &main_block->proc[proc_idx] );
|
||||
|
||||
shm_sem_done(&main_block->sem);
|
||||
shmdt( (void *) main_block);
|
||||
main_block= NULL;
|
||||
}
|
||||
|
||||
int DDE_no_of_attached()
|
||||
{
|
||||
struct shmid_ds shm_info;
|
||||
|
||||
if (shmctl(main_shm_id, IPC_STAT, &shm_info) == -1)
|
||||
return -1;
|
||||
|
||||
return shm_info.shm_nattch;
|
||||
}
|
||||
/*
|
||||
** Test if shm_id is MainBlock and attach it (if it is),
|
||||
** Return 1 if ok, 0 otherwise.
|
||||
*/
|
||||
static int attach_MainBlock(int shm_id)
|
||||
{
|
||||
struct shmid_ds shm_info;
|
||||
|
||||
if (shmctl(shm_id, IPC_STAT, &shm_info) == -1)
|
||||
return 0;
|
||||
|
||||
/* Make sure we don't work on somebody else's block */
|
||||
if (shm_info.shm_perm.cuid != getuid()) { /* creator is not me */
|
||||
WARN("Creator is not me!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
TRACE("shared memory exist, attaching anywhere\n");
|
||||
main_block=(struct shm_main_block *)shmat(shm_id, 0, 0);
|
||||
if ( (int)main_block==-1) {
|
||||
WARN("Attach failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(main_block->magic, shm_header) != 0) {
|
||||
TRACE("Detaching, wrong magic\n");
|
||||
shmdt((void *)main_block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (TRACE_ON(shm))
|
||||
print_shm_info(shm_id);
|
||||
|
||||
/* Is it an old unused block ? */
|
||||
if (shm_info.shm_nattch == 0) {
|
||||
TRACE("No attaches, deleting old data\n");
|
||||
shm_delete_all(shm_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait for data structure to stabilize */
|
||||
while (main_block->build_lock)
|
||||
usleep(10000);
|
||||
|
||||
main_shm_id= shm_id;
|
||||
|
||||
shm_main_refresh();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* (Function used by the constructor)
|
||||
* Try to get existing shared memory with key="Wine", size=SHM_MINBLOCK
|
||||
* complete user permission.
|
||||
* If such block is found - return true (1), else return false (0)
|
||||
*/
|
||||
static int shm_locate_MainBlock(key_t shm_key)
|
||||
{
|
||||
int shm_id; /* Descriptor to this shared memory */
|
||||
int i;
|
||||
|
||||
TRACE("trying to attach, key=0x%x\n",
|
||||
shm_key);
|
||||
for (i=0 ; i < SHM_KEY_RANGE ; i++) {
|
||||
TRACE("iteration=%d\n", i);
|
||||
|
||||
shm_id= shmget ( shm_key+i, SHM_MINBLOCK ,0700);
|
||||
|
||||
if (shm_id != -1) {
|
||||
if ( attach_MainBlock(shm_id) ) {
|
||||
return 1; /* success! */
|
||||
}
|
||||
} else {
|
||||
switch(errno) {
|
||||
#ifdef EIDRM
|
||||
case EIDRM: /* segment destroyed */
|
||||
#endif
|
||||
case EACCES: /* no user permision */
|
||||
break;
|
||||
|
||||
case ENOMEM: /* no free memory */
|
||||
case ENOENT: /* this key does not exist */
|
||||
default :
|
||||
WARN("shmget failed, errno=%d, %s\n",
|
||||
errno, strerror(errno) );
|
||||
return 0; /* Failed */
|
||||
}
|
||||
} /* if .. else */
|
||||
} /* for */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* (Function used by the constructor)
|
||||
* Try to allocate new shared memory with key="Wine", size=SHM_MINBLOCK
|
||||
* with complete user permission.
|
||||
* If allocation succeeds - return true (1), else return false (0)
|
||||
*/
|
||||
static int shm_create_MainBlock(key_t MainShmKey)
|
||||
{
|
||||
int shm_id;
|
||||
int flags= 0700 | IPC_CREAT | IPC_EXCL;
|
||||
int i;
|
||||
|
||||
TRACE("creating shared memory\n");
|
||||
|
||||
/* try to allocate shared memory with key="Wine", size=SHM_MINBLOCK, */
|
||||
/* complete user permission */
|
||||
for (i=0 ; i < SHM_KEY_RANGE ; i++) {
|
||||
shm_id= shmget ( (key_t) MainShmKey, SHM_MINBLOCK, flags);
|
||||
if (shm_id != -1)
|
||||
break;
|
||||
}
|
||||
if (shm_id == -1) {
|
||||
WARN("failed to create shared memory\n");
|
||||
return 0;
|
||||
}
|
||||
TRACE("shared memory created, attaching\n");
|
||||
main_block=(struct shm_main_block*) shmat(shm_id, 0,0);
|
||||
if (TRACE_ON(shm))
|
||||
print_shm_info(shm_id);
|
||||
main_shm_id= shm_id;
|
||||
shm_setup_main_block();
|
||||
dde_wnd_setup();
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* link to the dde shared memory block */
|
||||
/* RETURN: 0 on success, non zero on failure */
|
||||
int shm_init(void)
|
||||
{
|
||||
if ( !shm_locate_MainBlock(WineKey)
|
||||
&& !shm_create_MainBlock(WineKey)) {
|
||||
ERR("Failed to init main shm block\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dde_proc_add(main_block->proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shm_main_refresh()
|
||||
{
|
||||
int proc_idx;
|
||||
|
||||
for (proc_idx= 0 ; proc_idx < DDE_PROCS ; proc_idx++)
|
||||
dde_proc_refresh( &main_block->proc[proc_idx] );
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
139
ipc/shm_semaph.c
139
ipc/shm_semaph.c
|
@ -1,139 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_semaph.c
|
||||
* Purpose: Handle semaphores for shared memory operations.
|
||||
***************************************************************************
|
||||
*/
|
||||
#ifdef CONFIG_IPC
|
||||
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sem.h>
|
||||
#include <errno.h>
|
||||
#include "debugtools.h"
|
||||
#include "shm_semaph.h"
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(sem)
|
||||
#define SEM_READ 0
|
||||
#define SEM_WRITE 1
|
||||
|
||||
/* IMPORTANT: Make sure that killed process will not lock everything.
|
||||
* If possible, restrict usage of these functions.
|
||||
*/
|
||||
void shm_read_wait(shm_sem semid)
|
||||
{
|
||||
struct sembuf sop[2];
|
||||
int ret;
|
||||
|
||||
TRACE("(%d)\n",semid);
|
||||
sop[0].sem_num=SEM_READ;
|
||||
sop[0].sem_op=1; /* add this read instance */
|
||||
sop[0].sem_flg=SEM_UNDO; /* undo in case process dies */
|
||||
|
||||
sop[1].sem_num=SEM_WRITE;
|
||||
sop[1].sem_op=0; /* wait until no writing instance exists */
|
||||
sop[1].sem_flg=SEM_UNDO;
|
||||
|
||||
do {
|
||||
ret=semop (semid,sop , 2);
|
||||
} while (ret<0 && errno==EINTR); /* interrupted system call? */
|
||||
|
||||
if (ret<0)
|
||||
WARN("(semid=%d,errno=%d): Failed semaphore lock for read\n",
|
||||
semid, errno);
|
||||
}
|
||||
void shm_write_wait(shm_sem semid)
|
||||
{
|
||||
struct sembuf sop[3];
|
||||
int ret;
|
||||
|
||||
TRACE("(%d)\n",semid);
|
||||
sop[0].sem_num=SEM_READ;
|
||||
sop[0].sem_op=0; /* wait until no reading instance exist */
|
||||
sop[0].sem_flg=SEM_UNDO;
|
||||
|
||||
sop[1].sem_num=SEM_WRITE;
|
||||
sop[1].sem_op=1; /* writing is in progress - disable read */
|
||||
sop[1].sem_flg=SEM_UNDO; /* undo if process dies */
|
||||
|
||||
sop[2].sem_num=SEM_READ;
|
||||
sop[2].sem_op=1; /* disable new writes */
|
||||
sop[2].sem_flg=SEM_UNDO;
|
||||
|
||||
do {
|
||||
ret=semop (semid,sop , 3);
|
||||
} while (ret<0 && errno==EINTR); /* interrupted system call? */
|
||||
|
||||
if (ret<0) /* test for the error */
|
||||
WARN("(semid=%d,errno=%d): Failed semaphore lock for write\n",
|
||||
semid, errno);
|
||||
}
|
||||
void shm_write_signal(shm_sem semid)
|
||||
{
|
||||
struct sembuf sop[2];
|
||||
int ret;
|
||||
|
||||
TRACE("(%d)\n",semid);
|
||||
sop[0].sem_num=SEM_READ;
|
||||
sop[0].sem_op=-1;
|
||||
sop[0].sem_flg=IPC_NOWAIT | SEM_UNDO; /* no reason to wait */
|
||||
|
||||
sop[1].sem_num=SEM_WRITE;
|
||||
sop[1].sem_op=-1;
|
||||
sop[1].sem_flg=IPC_NOWAIT | SEM_UNDO; /* no reason to wait */
|
||||
|
||||
do {
|
||||
ret=semop (semid,sop , 2);
|
||||
} while (ret<0 && errno==EINTR); /* interrupted system call? */
|
||||
|
||||
if (ret<0) /* test for the error */
|
||||
WARN("(semid=%d,errno=%d): Failed semaphore unlock for write\n",
|
||||
semid, errno);
|
||||
}
|
||||
|
||||
void shm_read_signal(shm_sem semid)
|
||||
{
|
||||
struct sembuf sop[2];
|
||||
int ret;
|
||||
|
||||
TRACE("(%d)\n",semid);
|
||||
sop[0].sem_num=SEM_READ;
|
||||
sop[0].sem_op=-1;
|
||||
sop[0].sem_flg=IPC_NOWAIT | SEM_UNDO; /* no reason to wait */
|
||||
|
||||
do {
|
||||
ret=semop (semid,sop , 1);
|
||||
} while (ret<0 && errno==EINTR); /* interrupted system call? */
|
||||
|
||||
if (ret<0) /* test for the error */
|
||||
WARN("(semid=%d,errno=%d): Failed semaphore unlock for read\n",
|
||||
semid, errno);
|
||||
}
|
||||
|
||||
void shm_sem_init(shm_sem *sptr)
|
||||
{
|
||||
shm_sem semid;
|
||||
union semun arg;
|
||||
|
||||
semid=semget (IPC_PRIVATE, 2, 0700 | IPC_CREAT);
|
||||
|
||||
arg.val=0;
|
||||
semctl (semid, 0, SETVAL, arg);
|
||||
semctl (semid, 1, SETVAL, arg);
|
||||
*sptr=semid;
|
||||
}
|
||||
|
||||
void shm_sem_done(shm_sem *semptr)
|
||||
{
|
||||
union semun arg;
|
||||
|
||||
semctl (*semptr, 0, IPC_RMID , arg);
|
||||
semctl (*semptr, 1, IPC_RMID , arg);
|
||||
|
||||
*semptr= -1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IPC */
|
|
@ -1,127 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright 1995, Technion, Israel Institute of Technology
|
||||
* Electrical Eng, Software Lab.
|
||||
* Author: Michael Veksler.
|
||||
***************************************************************************
|
||||
* File: shm_semaph_test.c
|
||||
* Purpose: Test semaphores handleingr shared memory operations.
|
||||
***************************************************************************
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include "shm_semaph.h"
|
||||
#include <sys/shm.h>
|
||||
#define DEBUG_DEFINE_VARIABLES
|
||||
#include "debugtools.h"
|
||||
|
||||
static volatile int * volatile data;
|
||||
static int isparent=0;
|
||||
#define DELAY (rand()%10)
|
||||
shm_sem sem;
|
||||
|
||||
static void read_write(int num)
|
||||
{
|
||||
int i,j ;
|
||||
volatile float dummy=0;
|
||||
int val;
|
||||
|
||||
srand(num+time(NULL));
|
||||
for (i=0x3fff;i>=0;i--) {
|
||||
if((i&0x7ff)==0 && isparent)
|
||||
fprintf(stderr,"0x%06x\r",i);
|
||||
shm_write_wait(sem);
|
||||
*data= num;
|
||||
for (j=DELAY ; j>=0;j--)
|
||||
dummy*=2;
|
||||
if (*data!=num) {
|
||||
fprintf(stderr,"\nbad shm_write_wait(), num=%d\n",num);
|
||||
shm_write_signal(sem);
|
||||
return;
|
||||
}
|
||||
shm_write_signal(sem);
|
||||
for (j=DELAY ; j>=0 ;j--)
|
||||
dummy*=2;
|
||||
shm_read_wait(sem);
|
||||
val=*data;
|
||||
for (j=DELAY; j>=0 ;j--)
|
||||
dummy*=0.5;
|
||||
if (*data!=val) {
|
||||
fprintf(stderr,"\nbad shm_read_wait(), num=%d,val=%d,*data=%d\n",
|
||||
num,val,*data);
|
||||
shm_read_signal(sem);
|
||||
return;
|
||||
}
|
||||
shm_read_signal(sem);
|
||||
}
|
||||
if (isparent)
|
||||
fputc('\n',stderr);
|
||||
}
|
||||
static void child1()
|
||||
{
|
||||
read_write(2);
|
||||
}
|
||||
static void child2()
|
||||
{
|
||||
read_write(10);
|
||||
}
|
||||
static void parent()
|
||||
{
|
||||
isparent=1;
|
||||
read_write(60);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int shmid;
|
||||
int ret1, ret2;
|
||||
int pid1, pid2;
|
||||
int stat=0;
|
||||
|
||||
shm_sem_init(&sem);
|
||||
shmid=shmget(IPC_PRIVATE, 0x100, IPC_CREAT | 0700);
|
||||
data= (int *)shmat ( shmid, NULL, 0);
|
||||
*data=0;
|
||||
|
||||
switch (pid1=fork()) {
|
||||
case -1:
|
||||
perror("fork 1");
|
||||
return 1;
|
||||
case 0:
|
||||
fprintf(stderr,"child1\n");
|
||||
child1();
|
||||
fprintf(stderr,"child1 done\n");
|
||||
return 0;
|
||||
default :
|
||||
}
|
||||
switch (pid2=fork()) {
|
||||
case -1:
|
||||
perror("fork 2");
|
||||
stat|=1;
|
||||
break;
|
||||
case 0:
|
||||
fprintf(stderr,"child2\n");
|
||||
child2();
|
||||
fprintf(stderr,"child2 done\n");
|
||||
return 0;
|
||||
default :
|
||||
}
|
||||
fprintf(stderr,"parent\n");
|
||||
if (pid2>0) { /* if second fork did not fail */
|
||||
parent();
|
||||
fprintf(stderr,"parent done, waiting for child2\n");
|
||||
waitpid(pid2,&ret2,WUNTRACED);
|
||||
stat|=ret2;
|
||||
}
|
||||
fprintf(stderr,"parent done, waiting for child1\n");
|
||||
waitpid(pid1,&ret1,WUNTRACED);
|
||||
stat|=ret1;
|
||||
fprintf(stderr,"all done\n");
|
||||
|
||||
shmctl(shmid, IPC_RMID,NULL);
|
||||
shm_sem_done(&sem);
|
||||
return stat;
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include "dde.h"
|
||||
#include "windef.h"
|
||||
#include "global.h"
|
||||
#include <win.h>
|
||||
#define DEBUG_DEFINE_VARIABLES
|
||||
#define DEBUG_ALL
|
||||
#include "debugtools.h"
|
||||
|
||||
#define DDE_PROC2WIN(proc_idx) ( (HWND) ~( (proc_idx)+1) )
|
||||
#define DDE_WIN2PROC(win) ( (int) ~(short) ((win)+1) )
|
||||
#define DDE_IsRemoteWindow(win) ( (win)<0xffff && (win)>=(0xffff-DDE_PROCS))
|
||||
|
||||
|
||||
char *MessageTypeNames[0x400]={NULL};
|
||||
char *dummy_store_for_debug_msg_name;
|
||||
|
||||
ldt_copy_entry ldt_copy[LDT_SIZE];
|
||||
|
||||
int LDT_GetEntry( int entry, ldt_entry *content )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LDT_SetEntry( int entry, ldt_entry const *content )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dummy_usage_of_debug_msg_name()
|
||||
{
|
||||
dummy_store_for_debug_msg_name=debug_msg_name[0];
|
||||
}
|
||||
|
||||
/* stub */
|
||||
HWND GetDesktopWindow()
|
||||
{
|
||||
printf("GetDesktopWindow\n");
|
||||
return 0;
|
||||
}
|
||||
/* stub */
|
||||
/* smart stub */
|
||||
LONG SendMessage(HWND a,WORD b,WORD c,LONG d)
|
||||
{
|
||||
MSG msg;
|
||||
printf("SendMessage(%04x,%04x,%04x,%04lx)\n",a,b,c,d);
|
||||
if (DDE_IsRemoteWindow(a) || a==(HWND)-1)
|
||||
return 0;
|
||||
if (b!=WM_DDE_INITIATE)
|
||||
return 0;
|
||||
msg.hwnd=c;
|
||||
msg.message= WM_DDE_ACK;
|
||||
msg.lParam= 0;
|
||||
msg.wParam= 0;
|
||||
return DDE_SendMessage(&msg);
|
||||
}
|
||||
/* stub */
|
||||
BOOL PostMessage(HWND a,WORD b,WORD c,LONG d)
|
||||
{
|
||||
printf("PostMessage(%04x,%04x,%04x,%04lx)\n",a,b,c,d);
|
||||
return 0;
|
||||
}
|
||||
/* stub */
|
||||
HWND GetTopWindow(HWND a)
|
||||
{
|
||||
printf("GetTopWindow(%04x)\n",a);
|
||||
return 1;
|
||||
}
|
||||
/* stub */
|
||||
WORD FreeSelector16(WORD a)
|
||||
{
|
||||
printf("FreeSelector(%04x)\n",a);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stub that partially emulates the true GLOBAL_CreateBlock function */
|
||||
HGLOBAL16 GLOBAL_CreateBlock( WORD flags, void *ptr, DWORD size,
|
||||
HGLOBAL16 hOwner, BOOL isCode,
|
||||
BOOL is32Bit, BOOL isReadOnly,
|
||||
SHMDATA *shmdata )
|
||||
{
|
||||
|
||||
printf("GLOBAL_CreateBlock(flags=0x%x,ptr=0x%08lx, size=0x%x,hOwner=0x%x\n",
|
||||
(int)flags, (long)ptr, (int)size, (int)hOwner);
|
||||
printf("isCode=%d, is32Bit=%d, isReadOnly=%d, \n", isCode, is32Bit,
|
||||
isReadOnly);
|
||||
printf("*shmdata={handle=0x%x,sel=0x%x, shmid=%d})\n",
|
||||
shmdata->handle, shmdata->sel, shmdata->shmid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* stub */
|
||||
WND *WIN_FindWndPtr(HWND hwnd)
|
||||
{
|
||||
static WND win;
|
||||
printf("WIN_FindWndPtr(%d)\n",hwnd);
|
||||
if (hwnd==0)
|
||||
return NULL;
|
||||
win.next=NULL;
|
||||
win.dwStyle=WS_POPUP;
|
||||
|
||||
return &win;
|
||||
}
|
||||
|
||||
/* stub */
|
||||
WORD GetCurrentPDB16(void)
|
||||
{
|
||||
printf("GetCurrentPDB()\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stub */
|
||||
void Yield(void)
|
||||
{
|
||||
}
|
|
@ -187,10 +187,6 @@ BOOL SIGNAL_Init(void)
|
|||
sigemptyset(&async_signal_set);
|
||||
|
||||
SIGNAL_SetHandler( SIGCHLD, (void (*)())SIGNAL_child, 1);
|
||||
#ifdef CONFIG_IPC
|
||||
sigaddset(&async_signal_set, SIGUSR2);
|
||||
SIGNAL_SetHandler( SIGUSR2, (void (*)())stop_wait, 1); /* For IPC */
|
||||
#endif
|
||||
#ifdef SIGIO
|
||||
sigaddset(&async_signal_set, SIGIO);
|
||||
/* SIGNAL_SetHandler( SIGIO, (void (*)())WINSOCK_sigio, 0); */
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "syslevel.h"
|
||||
#include "debugtools.h"
|
||||
#include "dosexe.h"
|
||||
#include "dde_proc.h"
|
||||
#include "services.h"
|
||||
#include "server.h"
|
||||
|
||||
|
|
|
@ -26,10 +26,6 @@
|
|||
#include "user.h"
|
||||
#include "debugtools.h"
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
#include "dde_atom.h"
|
||||
#endif
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(atom)
|
||||
|
||||
#define DEFAULT_ATOMTABLE_SIZE 37
|
||||
|
@ -545,11 +541,7 @@ UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
|
|||
ATOM WINAPI GlobalAddAtom16( SEGPTR str )
|
||||
{
|
||||
if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */
|
||||
#ifdef CONFIG_IPC
|
||||
return DDE_GlobalAddAtom( str );
|
||||
#else
|
||||
return ATOM_AddAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -595,11 +587,7 @@ ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
|
|||
ATOM WINAPI GlobalDeleteAtom(
|
||||
ATOM atom /* [in] Atom to delete */
|
||||
) {
|
||||
#ifdef CONFIG_IPC
|
||||
return DDE_GlobalDeleteAtom( atom );
|
||||
#else
|
||||
return ATOM_DeleteAtom( ATOM_GlobalTable, atom );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -609,11 +597,7 @@ ATOM WINAPI GlobalDeleteAtom(
|
|||
ATOM WINAPI GlobalFindAtom16( SEGPTR str )
|
||||
{
|
||||
if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */
|
||||
#ifdef CONFIG_IPC
|
||||
return DDE_GlobalFindAtom( str );
|
||||
#else
|
||||
return ATOM_FindAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -652,11 +636,7 @@ ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
|
|||
*/
|
||||
UINT16 WINAPI GlobalGetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
|
||||
{
|
||||
#ifdef CONFIG_IPC
|
||||
return DDE_GlobalGetAtomName( atom, buffer, count );
|
||||
#else
|
||||
return (UINT16)ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "toolhelp.h"
|
||||
#include "selectors.h"
|
||||
#include "miscemu.h"
|
||||
#include "dde_mem.h"
|
||||
#include "stackframe.h"
|
||||
#include "module.h"
|
||||
#include "debugtools.h"
|
||||
|
@ -35,9 +34,6 @@ typedef struct
|
|||
BYTE pageLockCount; /* Count of GlobalPageLock() calls */
|
||||
BYTE flags; /* Allocation flags */
|
||||
BYTE selCount; /* Number of selectors allocated for this block */
|
||||
#ifdef CONFIG_IPC
|
||||
int shmid;
|
||||
#endif
|
||||
} GLOBALARENA;
|
||||
|
||||
/* Flags definitions */
|
||||
|
@ -124,22 +120,7 @@ HGLOBAL16 GLOBAL_CreateBlock( WORD flags, const void *ptr, DWORD size,
|
|||
|
||||
pArena->base = (DWORD)ptr;
|
||||
pArena->size = GET_SEL_LIMIT(sel) + 1;
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
if (flags & GMEM_DDESHARE)
|
||||
{
|
||||
pArena->handle = shmdata->handle;
|
||||
pArena->shmid = shmdata->shmid;
|
||||
shmdata->sel = sel;
|
||||
}
|
||||
else
|
||||
{
|
||||
pArena->handle = (flags & GMEM_MOVEABLE) ? sel - 1 : sel;
|
||||
pArena->shmid = 0;
|
||||
}
|
||||
#else
|
||||
pArena->handle = (flags & GMEM_MOVEABLE) ? sel - 1 : sel;
|
||||
#endif
|
||||
pArena->hOwner = hOwner;
|
||||
pArena->lockCount = 0;
|
||||
pArena->pageLockCount = 0;
|
||||
|
@ -226,15 +207,7 @@ HGLOBAL16 GLOBAL_Alloc( UINT16 flags, DWORD size, HGLOBAL16 hOwner,
|
|||
size = (size + 0x1f) & ~0x1f;
|
||||
|
||||
/* Allocate the linear memory */
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
if (flags & GMEM_DDESHARE)
|
||||
ptr = DDE_malloc(flags, size, &shmdata);
|
||||
else
|
||||
#endif /* CONFIG_IPC */
|
||||
{
|
||||
ptr = HeapAlloc( SystemHeap, 0, size );
|
||||
}
|
||||
/* FIXME: free discardable blocks and try again? */
|
||||
if (!ptr) return 0;
|
||||
|
||||
|
@ -252,50 +225,6 @@ HGLOBAL16 GLOBAL_Alloc( UINT16 flags, DWORD size, HGLOBAL16 hOwner,
|
|||
return handle;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
/***********************************************************************
|
||||
* GLOBAL_FindArena
|
||||
*
|
||||
* Find the arena for a given handle
|
||||
* (when handle is not serial - e.g. DDE)
|
||||
*/
|
||||
static GLOBALARENA *GLOBAL_FindArena( HGLOBAL16 handle)
|
||||
{
|
||||
int i;
|
||||
for (i = globalArenaSize-1 ; i>=0 ; i--) {
|
||||
if (pGlobalArena[i].size!=0 && pGlobalArena[i].handle == handle)
|
||||
return ( &pGlobalArena[i] );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* DDE_GlobalHandleToSel
|
||||
*/
|
||||
|
||||
WORD DDE_GlobalHandleToSel( HGLOBAL16 handle )
|
||||
{
|
||||
GLOBALARENA *pArena;
|
||||
SEGPTR segptr;
|
||||
|
||||
pArena= GLOBAL_FindArena(handle);
|
||||
if (pArena) {
|
||||
int ArenaIdx = pArena - pGlobalArena;
|
||||
|
||||
/* See if synchronized to the shared memory */
|
||||
return DDE_SyncHandle(handle, ( ArenaIdx << __AHSHIFT) | 7);
|
||||
}
|
||||
|
||||
/* attach the block */
|
||||
DDE_AttachHandle(handle, &segptr);
|
||||
|
||||
return SELECTOROF( segptr );
|
||||
}
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* GlobalAlloc16 (KERNEL.15)
|
||||
* RETURNS
|
||||
|
@ -335,14 +264,6 @@ HGLOBAL16 WINAPI GlobalReAlloc16(
|
|||
handle, size, flags );
|
||||
if (!handle) return 0;
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
if (flags & GMEM_DDESHARE || is_dde_handle(handle))
|
||||
{
|
||||
FIXME("shared memory reallocating unimplemented\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
if (!VALID_HANDLE(handle)) {
|
||||
WARN("Invalid handle 0x%04x!\n", handle);
|
||||
return 0;
|
||||
|
@ -456,9 +377,6 @@ HGLOBAL16 WINAPI GlobalFree16(
|
|||
|
||||
TRACE("%04x\n", handle );
|
||||
if (!GLOBAL_FreeBlock( handle )) return handle; /* failed */
|
||||
#ifdef CONFIG_IPC
|
||||
if (is_dde_handle(handle)) return DDE_GlobalFree(handle);
|
||||
#endif /* CONFIG_IPC */
|
||||
if (ptr) HeapFree( SystemHeap, 0, ptr );
|
||||
return 0;
|
||||
}
|
||||
|
@ -477,11 +395,6 @@ SEGPTR WINAPI WIN16_GlobalLock16( HGLOBAL16 handle )
|
|||
{
|
||||
if (handle == (HGLOBAL16)-1) handle = CURRENT_DS;
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
if (is_dde_handle(handle))
|
||||
return PTR_SEG_OFF_TO_SEGPTR( DDE_GlobalHandleToSel(handle), 0 );
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
if (!VALID_HANDLE(handle)) {
|
||||
WARN("Invalid handle 0x%04x passed to WIN16_GlobalLock16!\n",handle);
|
||||
return (SEGPTR)0;
|
||||
|
@ -511,9 +424,6 @@ LPVOID WINAPI GlobalLock16(
|
|||
if (!VALID_HANDLE(handle))
|
||||
return (LPVOID)0;
|
||||
GET_ARENA_PTR(handle)->lockCount++;
|
||||
#ifdef CONFIG_IPC
|
||||
if (is_dde_handle(handle)) return DDE_AttachHandle(handle, NULL);
|
||||
#endif
|
||||
return (LPVOID)GET_ARENA_PTR(handle)->base;
|
||||
}
|
||||
|
||||
|
@ -893,9 +803,6 @@ HANDLE16 WINAPI FarGetOwner16( HGLOBAL16 handle )
|
|||
WORD WINAPI GlobalHandleToSel16( HGLOBAL16 handle )
|
||||
{
|
||||
if (!handle) return 0;
|
||||
#ifdef CONFIG_IPC
|
||||
if (is_dde_handle(handle)) return DDE_GlobalHandleToSel(handle);
|
||||
#endif
|
||||
if (!VALID_HANDLE(handle)) {
|
||||
WARN("Invalid handle 0x%04x passed to GlobalHandleToSel!\n",handle);
|
||||
return 0;
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "ddeml.h"
|
||||
#include "winerror.h"
|
||||
#include "heap.h"
|
||||
#include "shm_semaph.h"
|
||||
#include "debugtools.h"
|
||||
#include "tchar.h"
|
||||
#include "winnt.h"
|
||||
|
|
|
@ -988,11 +988,6 @@ static BOOL MSG_PeekMessage( LPMSG msg, HWND hwnd, DWORD first, DWORD last,
|
|||
POINT16 pt16;
|
||||
int iWndsLocks;
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
DDE_TestDDE(hwnd); /* do we have dde handling in the window ?*/
|
||||
DDE_GetRemoteMessage();
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */
|
||||
if (first || last)
|
||||
{
|
||||
|
@ -1458,11 +1453,6 @@ BOOL WINAPI PostMessageA( HWND hwnd, UINT message, WPARAM wParam,
|
|||
msg.pt.x = 0;
|
||||
msg.pt.y = 0;
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
if (DDE_PostMessage(&msg))
|
||||
return TRUE;
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
if (hwnd == HWND_BROADCAST)
|
||||
{
|
||||
WND *pDesktop = WIN_GetDesktop();
|
||||
|
@ -1672,13 +1662,7 @@ LRESULT WINAPI SendMessage16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam,
|
|||
LPARAM lParam)
|
||||
{
|
||||
LRESULT res;
|
||||
#ifdef CONFIG_IPC
|
||||
MSG16 DDE_msg = { hwnd, msg, wParam, lParam };
|
||||
if (DDE_SendMessage(&DDE_msg)) return TRUE;
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
MSG_SendMessage(hwnd, msg, wParam, lParam, INFINITE, 0, &res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#include "nonclient.h"
|
||||
#include "queue.h"
|
||||
#include "winpos.h"
|
||||
#include "shm_main_blk.h"
|
||||
#include "dde_proc.h"
|
||||
#include "clipboard.h"
|
||||
#include "winproc.h"
|
||||
#include "task.h"
|
||||
|
@ -440,11 +438,6 @@ static WND* WIN_DestroyWindow( WND* wndPtr )
|
|||
|
||||
TRACE_(win)("%04x\n", wndPtr->hwndSelf );
|
||||
|
||||
#ifdef CONFIG_IPC
|
||||
if (main_block)
|
||||
DDE_DestroyWindow(wndPtr->hwndSelf);
|
||||
#endif /* CONFIG_IPC */
|
||||
|
||||
/* free child windows */
|
||||
WIN_LockWndPtr(wndPtr->child);
|
||||
while ((pWnd = wndPtr->child))
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "callback.h"
|
||||
#include "clipboard.h"
|
||||
#include "dce.h"
|
||||
#include "dde_proc.h"
|
||||
#include "debugtools.h"
|
||||
#include "drive.h"
|
||||
#include "heap.h"
|
||||
|
|
Loading…
Reference in New Issue