logo       

CVS: sbcl/src/code cold-init.lisp,1.42,1.43 cross-thread.lisp,1.1,1.2 gc.li: msg#00135

Subject: CVS: sbcl/src/code cold-init.lisp,1.42,1.43 cross-thread.lisp,1.1,1.2 gc.lisp,1.53,1.54 target-thread.lisp,1.12,1.13 target-unithread.lisp,1.6,1.7 thread.lisp,1.8,1.9 toplevel.lisp,1.55,1.56
Update of /cvsroot/sbcl/sbcl/src/code
In directory sc8-pr-cvs1:/tmp/cvs-serv26651/src/code

Modified Files:
        cold-init.lisp cross-thread.lisp gc.lisp target-thread.lisp 
        target-unithread.lisp thread.lisp toplevel.lisp 
Log Message:
0.8.6.5
        "Well, the hours are pretty good"
        
        Merged the resistance-is-futex branch: see commit messages on
        branch for scary details

        "... but now I come to think about it, most of the actual minutes
        are pretty lousy"


Index: cold-init.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/cold-init.lisp,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -d -r1.42 -r1.43
--- cold-init.lisp      28 Aug 2003 15:32:28 -0000      1.42
+++ cold-init.lisp      27 Nov 2003 06:21:04 -0000      1.43
@@ -289,6 +289,7 @@
       ;; disabled by default. Joe User can explicitly enable them if
       ;; desired.
       (set-floating-point-modes :traps '(:overflow :invalid :divide-by-zero))
+      (sb!thread::maybe-install-futex-functions)
 
       ;; Clear pseudo atomic in case this core wasn't compiled with
       ;; support.

Index: cross-thread.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/cross-thread.lisp,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- cross-thread.lisp   2 Apr 2003 11:15:12 -0000       1.1
+++ cross-thread.lisp   27 Nov 2003 06:21:04 -0000      1.2
@@ -5,3 +5,5 @@
 (defmacro with-recursive-lock ((mutex) &body body)
   `(progn ,@body))
 
+
+

Index: gc.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/gc.lisp,v
retrieving revision 1.53
retrieving revision 1.54
diff -u -d -r1.53 -r1.54
--- gc.lisp     25 Oct 2003 21:34:36 -0000      1.53
+++ gc.lisp     27 Nov 2003 06:21:04 -0000      1.54
@@ -230,28 +230,27 @@
 
 ;;; For GENCGC all generations < GEN will be GC'ed.
 
-(defvar *already-in-gc* nil "System is running SUB-GC")
-(defvar *gc-mutex* (sb!thread:make-mutex :name "GC Mutex"))
+(defvar *already-in-gc* 
+  (sb!thread:make-mutex :name "GC lock") "ID of thread running SUB-GC")
 
 (defun sub-gc (&key (gen 0) &aux (pre-gc-dynamic-usage (dynamic-usage)))
-  ;; catch attempts to gc recursively or during post-hooks and ignore them
-  (when (sb!thread::mutex-value *gc-mutex*)  (return-from sub-gc nil))
-  (sb!thread:with-mutex (*gc-mutex* :wait-p nil)
+  (let ((me (sb!thread:current-thread-id)))
+    (when (eql (sb!thread::mutex-value *already-in-gc*) me) 
+      (return-from sub-gc nil))
     (setf *need-to-collect-garbage* t)
     (when (zerop *gc-inhibit*)
-      (without-interrupts
-       (gc-stop-the-world)
-       (collect-garbage gen)
-       (incf *n-bytes-freed-or-purified*
-            (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
-       (setf *need-to-collect-garbage* nil)
-       (gc-start-the-world))
-      (scrub-control-stack)
-      (setf *need-to-collect-garbage* nil)
-      (dolist (h *after-gc-hooks*) (carefully-funcall h))))
-  (values))
-       
-
+      (loop
+       (sb!thread:with-mutex (*already-in-gc*)
+        (unless *need-to-collect-garbage* (return-from sub-gc nil))
+        (without-interrupts
+         (gc-stop-the-world)
+         (collect-garbage gen)
+         (incf *n-bytes-freed-or-purified*
+               (max 0 (- pre-gc-dynamic-usage (dynamic-usage))))
+         (scrub-control-stack)
+         (setf *need-to-collect-garbage* nil)
+         (dolist (h *after-gc-hooks*) (carefully-funcall h))
+         (gc-start-the-world)))))))
 
 ;;; This is the user-advertised garbage collection function.
 (defun gc (&key (gen 0) (full nil) &allow-other-keys)

Index: target-thread.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/target-thread.lisp,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- target-thread.lisp  2 Oct 2003 23:13:09 -0000       1.12
+++ target-thread.lisp  27 Nov 2003 06:21:04 -0000      1.13
@@ -1,9 +1,18 @@
 (in-package "SB!THREAD")
 
+;;; FIXME it would be good to define what a thread id is or isn't (our
+;;; current assumption is that it's a fixnum).  It so happens that on
+;;; Linux it's a pid, but it might not be on posix thread implementations
+
 (sb!alien::define-alien-routine ("create_thread" %create-thread)
      sb!alien:unsigned-long
   (lisp-fun-address sb!alien:unsigned-long))
 
+(sb!alien::define-alien-routine "signal_thread_to_dequeue"
+    sb!alien:unsigned-int
+  (thread-pid sb!alien:unsigned-long))
+
+
 (defun make-thread (function)
   (let ((real-function (coerce function 'function)))
     (%create-thread
@@ -106,17 +115,43 @@
 
 ;;;; the higher-level locking operations are based on waitqueues
 
+(declaim (inline waitqueue-data-address mutex-value-address))
+
 (defstruct waitqueue
   (name nil :type (or null simple-base-string))
   (lock 0)
   (data nil))
 
+;;; The bare 4 here and 5 below are offsets of the slots in the struct.
+;;; There ought to be some better way to get these numbers
+(defun waitqueue-data-address (lock)
+  (declare (optimize (speed 3)))
+  (sb!ext:truly-the
+   (unsigned-byte 32)
+   (+ (sb!kernel:get-lisp-obj-address lock)
+      (- (* 4 sb!vm:n-word-bytes) sb!vm:instance-pointer-lowtag))))
+
 (defstruct (mutex (:include waitqueue))
   (value nil))
 
+(defun mutex-value-address (lock)
+  (declare (optimize (speed 3)))
+  (sb!ext:truly-the
+   (unsigned-byte 32)
+   (+ (sb!kernel:get-lisp-obj-address lock)
+      (- (* 5 sb!vm:n-word-bytes) sb!vm:instance-pointer-lowtag))))
+
 (sb!alien:define-alien-routine "block_sigcont"  void)
 (sb!alien:define-alien-routine "unblock_sigcont_and_sleep"  void)
 
+#!+sb-futex
+(declaim (inline futex-wait futex-wake))
+#!+sb-futex
+(sb!alien:define-alien-routine
+    "futex_wait" int (word unsigned-long) (old-value unsigned-long))
+#!+sb-futex
+(sb!alien:define-alien-routine
+    "futex_wake" int (word unsigned-long) (n unsigned-long))
 
 ;;; this should only be called while holding the queue spinlock.
 ;;; it releases the spinlock before sleeping
@@ -140,13 +175,14 @@
 ;;; this should only be called while holding the queue spinlock.
 (defun signal-queue-head (queue)
   (let ((p (car (waitqueue-data queue))))
-    (when p (sb!unix:unix-kill p  sb!unix::sig-dequeue))))
+    (when p (signal-thread-to-dequeue p))))
 
 ;;;; mutex
 
+;;; i suspect there may be a race still in this: the futex version requires
+;;; the old mutex value before sleeping, so how do we get away without it
 (defun get-mutex (lock &optional new-value (wait-p t))
-  (declare (type mutex lock)
-          (optimize (speed 3)))
+  (declare (type mutex lock) (optimize (speed 3)))
   (let ((pid (current-thread-id)))
     (unless new-value (setf new-value pid))
     (assert (not (eql new-value (mutex-value lock))))
@@ -163,6 +199,21 @@
        (return nil))
      (wait-on-queue lock nil))))
 
+#!+sb-futex
+(defun get-mutex/futex (lock &optional new-value (wait-p t))
+  (declare (type mutex lock)  (optimize (speed 3)))
+  (let ((pid (current-thread-id))
+       old)
+    (unless new-value (setf new-value pid))
+    (assert (not (eql new-value (mutex-value lock))))
+    (loop
+     (unless
+        (setf old (sb!vm::%instance-set-conditional lock 4 nil new-value))
+       (return t))
+     (unless wait-p (return nil))
+     (futex-wait (mutex-value-address lock)
+                (sb!kernel:get-lisp-obj-address old)))))
+
 (defun release-mutex (lock &optional (new-value nil))
   (declare (type mutex lock))
   ;; we assume the lock is ours to release
@@ -170,6 +221,12 @@
     (setf (mutex-value lock) new-value)
     (signal-queue-head lock)))
 
+#!+sb-futex
+(defun release-mutex/futex (lock)
+  (declare (type mutex lock))
+  (setf (mutex-value lock) nil)
+  (futex-wake (mutex-value-address lock) 1))
+
 
 (defmacro with-mutex ((mutex &key value (wait-p t))  &body body)
   (with-unique-names (got)
@@ -200,10 +257,68 @@
        (dequeue queue))
       (get-mutex lock value))))
 
+#!+sb-futex
+(defun condition-wait/futex (queue lock)
+  (assert lock)
+  (let ((value (mutex-value lock)))
+    (unwind-protect
+        (let ((me (current-thread-id)))
+          ;; XXX we should do something to ensure that the result of this setf
+          ;; is visible to all CPUs
+          (setf (waitqueue-data queue) me)
+          (release-mutex lock)
+          ;; Now we go to sleep using futex-wait.  If anyone else
+          ;; manages to grab LOCK and call CONDITION-NOTIFY during
+          ;; this comment, it will change queue->data, and so
+          ;; futex-wait returns immediately instead of sleeping.
+          ;; Ergo, no lost wakeup
+          (futex-wait (waitqueue-data-address queue)
+                      (sb!kernel:get-lisp-obj-address me)))
+      ;; If we are interrupted while waiting, we should do these things
+      ;; before returning.  Ideally, in the case of an unhandled signal,
+      ;; we should do them before entering the debugger, but this is
+      ;; better than nothing.
+      (get-mutex lock value))))
+
+
 (defun condition-notify (queue)
   "Notify one of the processes waiting on QUEUE"
   (with-spinlock (queue) (signal-queue-head queue)))
 
+#!+sb-futex
+(defun condition-notify/futex (queue)
+  "Notify one of the processes waiting on QUEUE."
+  (let ((me (current-thread-id)))
+    ;; no problem if >1 thread notifies during the comment in
+    ;; condition-wait: as long as the value in queue-data isn't the
+    ;; waiting thread's id, it matters not what it is
+    ;; XXX we should do something to ensure that the result of this setf
+    ;; is visible to all CPUs
+    (setf (waitqueue-data queue) me)
+    (futex-wake (waitqueue-data-address queue) 1)))
+
+#!+sb-futex
+(defun condition-broadcast/futex (queue)
+  (let ((me (current-thread-id)))
+    (setf (waitqueue-data queue) me)
+    (futex-wake (waitqueue-data-address queue) (ash 1 30))))
+
+(defun condition-broadcast (queue)
+  "Notify all of the processes waiting on QUEUE."
+  (with-spinlock (queue)
+    (map nil #'signal-thread-to-dequeue (waitqueue-data queue))))
+
+;;; Futexes may be available at compile time but not runtime, so we
+;;; default to not using them unless os_init says they're available
+(defun maybe-install-futex-functions ()
+  #!+sb-futex
+  (unless (zerop (extern-alien "linux_supports_futex" int))
+    (setf (fdefinition 'get-mutex) #'get-mutex/futex
+         (fdefinition 'release-mutex) #'release-mutex/futex
+         (fdefinition 'condition-wait) #'condition-wait/futex
+         (fdefinition 'condition-broadcast) #'condition-broadcast/futex
+         (fdefinition 'condition-notify) #'condition-notify/futex)
+    t))
 
 ;;;; multiple independent listeners
 
@@ -239,170 +354,52 @@
   
 ;;;; job control
 
-(defvar *background-threads-wait-for-debugger* t)
-;;; may be T, NIL, or a function called with a stream and thread id 
-;;; as its two arguments, returning NIl or T
+
+(defvar *interactive-threads-lock* 
+  (make-mutex :name "*interactive-threads* lock"))
+(defvar *interactive-threads* nil)
+(defvar *interactive-threads-queue*
+  (make-waitqueue :name "All threads that need the terminal.  First ID on this 
list is running, the others are waiting"))
+
+(defun init-job-control ()
+  (with-mutex (*interactive-threads-lock*)
+    (setf *interactive-threads* (list (current-thread-id)))
+    (return-from init-job-control t)))
 
 ;;; called from top of invoke-debugger
 (defun debugger-wait-until-foreground-thread (stream)
   "Returns T if thread had been running in background, NIL if it was
-already the foreground thread, or transfers control to the first applicable
-restart if *BACKGROUND-THREADS-WAIT-FOR-DEBUGGER* says to do that instead"
-  (let* ((wait-p *background-threads-wait-for-debugger*)
-        (*background-threads-wait-for-debugger* nil)
-        (lock *session-lock*))
-    (when (not (eql (mutex-value lock)   (CURRENT-THREAD-ID)))
-      (when (functionp wait-p) 
-       (setf wait-p 
-             (funcall wait-p stream (CURRENT-THREAD-ID))))
-      (cond (wait-p (get-foreground))
-           (t  (invoke-restart (car (compute-restarts))))))))
+interactive."
+  (prog1
+      (with-mutex (*interactive-threads-lock*)
+       (not (member (current-thread-id) *interactive-threads*)))
+    (get-foreground)))
 
-;;; install this with
-;;; (setf SB-INT:*REPL-PROMPT-FUN* #'sb-thread::thread-repl-prompt-fun)
-;;; One day it will be default
 (defun thread-repl-prompt-fun (out-stream)
-  (let ((lock *session-lock*))
-    (get-foreground)
-    (let ((stopped-threads (waitqueue-data lock)))
-      (when stopped-threads
-       (format out-stream "~{~&Thread ~A suspended~}~%" stopped-threads))
-      (sb!impl::repl-prompt-fun out-stream))))
-
-(defun resume-stopped-thread (id)
-  (let ((lock *session-lock*)) 
-    (with-spinlock (lock)
-      (setf (waitqueue-data lock)
-           (cons id (delete id  (waitqueue-data lock)))))
-    (release-foreground)))
-
-(defstruct rwlock
-  (name nil :type (or null simple-base-string))
-  (value 0 :type fixnum)
-  (max-readers nil :type (or fixnum null))
-  (max-writers 1 :type fixnum))
-#+nil
-(macrolet
-    ((make-rwlocking-function (lock-fn unlock-fn increment limit test)
-       (let ((do-update '(when (eql old-value
-                               (sb!vm::%instance-set-conditional
-                                lock 2 old-value new-value))
-                         (return (values t old-value))))
-            (vars `((timeout (and timeout (+ (get-internal-real-time) 
timeout)))
-                    old-value
-                    new-value
-                    (limit ,limit))))
-        (labels ((do-setfs (v) `(setf old-value (rwlock-value lock)
-                                 new-value (,v old-value ,increment))))
-          `(progn
-            (defun ,lock-fn (lock timeout)
-              (declare (type rwlock lock))
-              (let ,vars
-                (loop
-                 ,(do-setfs '+)
-                 (when ,test
-                   ,do-update)
-                 (when (sleep-a-bit timeout) (return nil)) ;expired
-                 )))
-            ;; unlock doesn't need timeout or test-in-range
-            (defun ,unlock-fn (lock)
-              (declare (type rwlock lock))
-              (declare (ignorable limit))
-              (let ,(cdr vars)
-                (loop
-                 ,(do-setfs '-)
-                 ,do-update))))))))
-    
-  (make-rwlocking-function %lock-for-reading %unlock-for-reading 1
-                          (rwlock-max-readers lock)
-                          (and (>= old-value 0)
-                               (or (null limit) (<= new-value limit))))
-  (make-rwlocking-function %lock-for-writing %unlock-for-writing -1
-                          (- (rwlock-max-writers lock))
-                          (and (<= old-value 0)
-                               (>= new-value limit))))
-#+nil  
-(defun get-rwlock (lock direction &optional timeout)
-  (ecase direction
-    (:read (%lock-for-reading lock timeout))
-    (:write (%lock-for-writing lock timeout))))
-#+nil
-(defun free-rwlock (lock direction)
-  (ecase direction
-    (:read (%unlock-for-reading lock))
-    (:write (%unlock-for-writing lock))))
-
-;;;; beyond this point all is commented.
-
-;;; Lock-Wait-With-Timeout  --  Internal
-;;;
-;;; Wait with a timeout for the lock to be free and acquire it for the
-;;; *current-process*.
-;;;
-#+nil
-(defun lock-wait-with-timeout (lock whostate timeout)
-  (declare (type lock lock))
-  (process-wait-with-timeout
-   whostate timeout
-   #'(lambda ()
-       (declare (optimize (speed 3)))
-       #-i486
-       (unless (lock-process lock)
-        (setf (lock-process lock) *current-process*))
-       #+i486
-       (null (kernel:%instance-set-conditional
-             lock 2 nil *current-process*)))))
-
-;;; With-Lock-Held  --  Public
-;;;
-#+nil
-(defmacro with-lock-held ((lock &optional (whostate "Lock Wait")
-                               &key (wait t) timeout)
-                         &body body)
-  "Execute the body with the lock held. If the lock is held by another
-  process then the current process waits until the lock is released or
-  an optional timeout is reached. The optional wait timeout is a time in
-  seconds acceptable to process-wait-with-timeout.  The results of the
-  body are return upon success and NIL is return if the timeout is
-  reached. When the wait key is NIL and the lock is held by another
-  process then NIL is return immediately without processing the body."
-  (let ((have-lock (gensym)))
-    `(let ((,have-lock (eq (lock-process ,lock) *current-process*)))
-      (unwind-protect
-          ,(cond ((and timeout wait)
-                  `(progn
-                     (when (and (error-check-lock-p ,lock) ,have-lock)
-                       (error "Dead lock"))
-                     (when (or ,have-lock
-                                #+i486 (null (kernel:%instance-set-conditional
-                                              ,lock 2 nil *current-process*))
-                                #-i486 (seize-lock ,lock)
-                                (if ,timeout
-                                    (lock-wait-with-timeout
-                                     ,lock ,whostate ,timeout)
-                                    (lock-wait ,lock ,whostate)))
-                       ,@body)))
-                 (wait
-                  `(progn
-                     (when (and (error-check-lock-p ,lock) ,have-lock)
-                       (error "Dead lock"))
-                     (unless (or ,have-lock
-                                #+i486 (null (kernel:%instance-set-conditional
-                                              ,lock 2 nil *current-process*))
-                                #-i486 (seize-lock ,lock))
-                       (lock-wait ,lock ,whostate))
-                     ,@body))
-                 (t
-                  `(when (or (and (recursive-lock-p ,lock) ,have-lock)
-                             #+i486 (null (kernel:%instance-set-conditional
-                                           ,lock 2 nil *current-process*))
-                             #-i486 (seize-lock ,lock))
-                     ,@body)))
-       (unless ,have-lock
-         #+i486 (kernel:%instance-set-conditional
-                 ,lock 2 *current-process* nil)
-         #-i486 (when (eq (lock-process ,lock) *current-process*)
-                  (setf (lock-process ,lock) nil)))))))
-
+  (get-foreground)
+  (let ((stopped-threads (cdr *interactive-threads*)))
+    (when stopped-threads
+      (format out-stream "~{~&Thread ~A suspended~}~%" stopped-threads))
+    (sb!impl::repl-prompt-fun out-stream)))
 
+(defun get-foreground ()
+  (loop
+   (with-mutex (*interactive-threads-lock*)
+     (let ((tid (current-thread-id)))
+       (when (eql (car *interactive-threads*) tid)
+        (sb!sys:enable-interrupt sb!unix:sigint #'sb!unix::sigint-handler)
+        (return-from get-foreground t))
+       (unless (member tid *interactive-threads*)
+        (setf (cdr (last *interactive-threads*)) (list tid)))
+       (condition-wait
+       *interactive-threads-queue* *interactive-threads-lock* )))))
 
+(defun release-foreground (&optional next)
+  "Background this thread.  If NEXT is supplied, arrange for it to have the 
foreground next"
+  (with-mutex (*interactive-threads-lock*)
+    (let ((tid (current-thread-id)))
+      (setf *interactive-threads* (delete tid *interactive-threads*))
+      (sb!sys:enable-interrupt sb!unix:sigint :ignore)
+      (when next (setf *interactive-threads*
+                      (list* next (delete next *interactive-threads*))))
+      (condition-broadcast *interactive-threads-queue*))))
\ No newline at end of file

Index: target-unithread.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/target-unithread.lisp,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- target-unithread.lisp       1 Sep 2003 07:55:42 -0000       1.6
+++ target-unithread.lisp       27 Nov 2003 06:21:04 -0000      1.7
@@ -122,10 +122,9 @@
   (signal-queue-head queue))
 
 
-;;;; multiple independent listeners
-
-(defvar *session-lock* nil)
-
 ;;;; job control
 
 (defun debugger-wait-until-foreground-thread (stream) t)
+(defun get-foreground () t)
+(defun release-foreground (&optional next) t)
+

Index: thread.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/thread.lisp,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- thread.lisp 1 Sep 2003 07:55:42 -0000       1.8
+++ thread.lisp 27 Nov 2003 06:21:04 -0000      1.9
@@ -1,7 +1,5 @@
 (in-package "SB!THREAD")
 
-(defvar *session-lock*)
-
 (sb!xc:defmacro with-recursive-lock ((mutex) &body body)
   #!+sb-thread
   (with-unique-names (cfp)
@@ -28,19 +26,3 @@
   #!-sb-thread
   `(progn ,@body))
 
-#!+sb-thread
-(defun get-foreground ()
-  (when (not (eql (mutex-value *session-lock*) (current-thread-id)))
-    (get-mutex *session-lock*))
-  (sb!sys:enable-interrupt sb!unix:sigint #'sb!unix::sigint-handler)
-  t)
-#!-sb-thread
-(defun get-foreground () t)
-
-#!+sb-thread
-(defun release-foreground ()
-  (sb!sys:enable-interrupt sb!unix:sigint :ignore)
-  (release-mutex *session-lock*)
-  t)
-#!-sb-thread
-(defun release-foreground () t)

Index: toplevel.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/toplevel.lisp,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -d -r1.55 -r1.56
--- toplevel.lisp       16 Nov 2003 23:52:04 -0000      1.55
+++ toplevel.lisp       27 Nov 2003 06:21:04 -0000      1.56
@@ -296,7 +296,7 @@
 (defun toplevel-init ()
 
   (/show0 "entering TOPLEVEL-INIT")
-  (setf sb!thread::*session-lock* (sb!thread:make-mutex :name "the terminal"))
+  (sb!thread::init-job-control)
   (sb!thread::get-foreground)
   (let (;; value of --sysinit option
        (sysinit nil)



-------------------------------------------------------
This SF.net email is sponsored by: SF.net Giveback Program.
Does SourceForge.net help you be more productive?  Does it
help you create better code?  SHARE THE LOVE, and help us help
YOU!  Click Here: http://sourceforge.net/donate/


<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Recently Viewed:
linux.arklinux....    user-groups.lin...    kde.usability/2...    ietf.ipp/2002-0...    mail.spam.spamc...    os.netbsd.devel...    audio.cd-record...    text.unicode.de...    php.documentati...    games.fps.halfl...    window-managers...    suse.oracle.gen...    bug-tracking.gn...    video.dvdrip.us...    xfree86.cvs/200...    java.netbeans.m...    network.argus/2...    culture.sf.kill...    debian.ports.al...    freebsd.questio...    qplus.devel/200...    handhelds.palm....   
Home | blog view | USPTO Patent Archive | advertise | OSDir is an inevitable website. super tiny logo

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe